]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'fc/zsh-completion'
authorJunio C Hamano <gitster@pobox.com>
Mon, 9 Nov 2020 22:06:29 +0000 (14:06 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 9 Nov 2020 22:06:29 +0000 (14:06 -0800)
Zsh autocompletion (in contrib/) update.

* fc/zsh-completion: (29 commits)
  zsh: update copyright notices
  completion: bash: remove old compat wrappers
  completion: bash: cleanup cygwin check
  completion: bash: trivial cleanup
  completion: zsh: add simple version check
  completion: zsh: trivial simplification
  completion: zsh: add alias descriptions
  completion: zsh: improve command tags
  completion: zsh: refactor command completion
  completion: zsh: shuffle functions around
  completion: zsh: simplify file_direct
  completion: zsh: simplify nl_append
  completion: zsh: trivial cleanup
  completion: zsh: simplify direct compadd
  completion: zsh: simplify compadd functions
  completion: zsh: fix splitting of words
  completion: zsh: add missing direct_append
  completion: fix conflict with bashcomp
  completion: zsh: fix completion for --no-.. options
  completion: bash: remove zsh wrapper
  ...

211 files changed:
.github/workflows/check-whitespace.yml [new file with mode: 0644]
.github/workflows/main.yml
Documentation/MyFirstContribution.txt
Documentation/RelNotes/2.29.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.30.0.txt
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/config/advice.txt
Documentation/config/checkout.txt
Documentation/config/clone.txt [new file with mode: 0644]
Documentation/config/core.txt
Documentation/config/format.txt
Documentation/config/maintenance.txt
Documentation/config/push.txt
Documentation/diff-options.txt
Documentation/git-am.txt
Documentation/git-checkout.txt
Documentation/git-cherry-pick.txt
Documentation/git-clone.txt
Documentation/git-commit-graph.txt
Documentation/git-commit.txt
Documentation/git-diff-index.txt
Documentation/git-diff-tree.txt
Documentation/git-diff.txt
Documentation/git-format-patch.txt
Documentation/git-maintenance.txt
Documentation/git-push.txt
Documentation/git-rebase.txt
Documentation/git-remote.txt
Documentation/git-restore.txt
Documentation/git-revert.txt
Documentation/git-send-email.txt
Documentation/git-svn.txt
Documentation/git-switch.txt
Documentation/git-worktree.txt
Documentation/gitattributes.txt
Documentation/githooks.txt
Documentation/merge-options.txt
Documentation/signoff-option.txt [new file with mode: 0644]
Documentation/technical/directory-rename-detection.txt
Makefile
add-patch.c
advice.c
advice.h
apply.c
blame.c
builtin/am.c
builtin/bisect--helper.c
builtin/blame.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/credential.c
builtin/diff-index.c
builtin/diff-tree.c
builtin/diff.c
builtin/fast-import.c
builtin/gc.c
builtin/grep.c
builtin/log.c
builtin/merge-tree.c
builtin/merge.c
builtin/pull.c
builtin/push.c
builtin/rebase.c
builtin/remote.c
builtin/revert.c
builtin/send-pack.c
builtin/worktree.c
commit-graph.c
commit.c
config.mak.dev
contrib/completion/git-completion.bash
contrib/git-resurrect.sh
diff-lib.c
diff.c
diff.h
dir.c
fmt-merge-msg.c
git-add--interactive.perl
git-bisect.sh
git-compat-util.h
git-svn.perl
line-log.c
midx.c
perl/FromCPAN/Error.pm
perl/Git.pm
perl/Git/I18N.pm
perl/Git/IndexInfo.pm
perl/Git/LoadCPAN.pm
perl/Git/LoadCPAN/Error.pm
perl/Git/LoadCPAN/Mail/Address.pm
perl/Git/Packet.pm
perl/Git/SVN.pm
perl/Git/SVN/Editor.pm
perl/Git/SVN/Fetcher.pm
perl/Git/SVN/GlobSpec.pm
perl/Git/SVN/Log.pm
perl/Git/SVN/Memoize/YAML.pm
perl/Git/SVN/Migration.pm
perl/Git/SVN/Prompt.pm
perl/Git/SVN/Ra.pm
perl/Git/SVN/Utils.pm
pkt-line.c
ref-filter.c
refs.c
refspec.c
refspec.h
remote-curl.c
remote.c
remote.h
repo-settings.c
repository.h
send-pack.c
sequencer.c
sequencer.h
sideband.c
sideband.h
t/Makefile
t/README
t/helper/test-pkt-line.c
t/lib-submodule-update.sh
t/perf/Makefile
t/perf/README
t/perf/p3400-rebase.sh
t/perf/p7519-fsmonitor.sh
t/t0000-basic.sh
t/t0001-init.sh
t/t0070-fundamental.sh
t/t0300-credentials.sh
t/t1400-update-ref.sh
t/t2004-checkout-cache-temp.sh
t/t2006-checkout-index-basic.sh
t/t2016-checkout-patch.sh
t/t2024-checkout-dwim.sh
t/t2060-switch.sh
t/t2071-restore-patch.sh
t/t2200-add-update.sh
t/t2402-worktree-list.sh
t/t3200-branch.sh
t/t3201-branch-contains.sh
t/t3203-branch-output.sh
t/t3205-branch-color.sh
t/t3435-rebase-gpg-sign.sh
t/t3920-crlf-messages.sh [new file with mode: 0755]
t/t4013-diff-various.sh
t/t4013/diff.log_-IA_-IB_-I1_-I2_-p_master [new file with mode: 0644]
t/t4018-diff-funcname.sh
t/t4018/bash-arithmetic-function [new file with mode: 0644]
t/t4018/bash-bashism-style-compact [new file with mode: 0644]
t/t4018/bash-bashism-style-function [new file with mode: 0644]
t/t4018/bash-bashism-style-whitespace [new file with mode: 0644]
t/t4018/bash-conditional-function [new file with mode: 0644]
t/t4018/bash-missing-parentheses [new file with mode: 0644]
t/t4018/bash-mixed-style-compact [new file with mode: 0644]
t/t4018/bash-mixed-style-function [new file with mode: 0644]
t/t4018/bash-nested-functions [new file with mode: 0644]
t/t4018/bash-other-characters [new file with mode: 0644]
t/t4018/bash-posix-style-compact [new file with mode: 0644]
t/t4018/bash-posix-style-function [new file with mode: 0644]
t/t4018/bash-posix-style-whitespace [new file with mode: 0644]
t/t4018/bash-subshell-function [new file with mode: 0644]
t/t4018/bash-trailing-comment [new file with mode: 0644]
t/t4018/css-attribute-value-selector [new file with mode: 0644]
t/t4018/css-block-level-@-statements [new file with mode: 0644]
t/t4018/css-class-selector [new file with mode: 0644]
t/t4018/css-id-selector [new file with mode: 0644]
t/t4018/css-root-selector [new file with mode: 0644]
t/t4018/php-abstract-method [new file with mode: 0644]
t/t4018/php-final-method [new file with mode: 0644]
t/t4018/rust-macro-rules [new file with mode: 0644]
t/t4068-diff-symmetric-merge-base.sh [new file with mode: 0755]
t/t4068-diff-symmetric.sh [deleted file]
t/t4114-apply-typechange.sh
t/t4127-apply-same-fn.sh
t/t5319-multi-pack-index.sh
t/t5324-split-commit-graph.sh
t/t5505-remote.sh
t/t5510-fetch.sh
t/t5526-fetch-submodules.sh
t/t5533-push-cas.sh
t/t5606-clone-options.sh
t/t5703-upload-pack-ref-in-want.sh
t/t6006-rev-list-format.sh
t/t6012-rev-list-simplify.sh
t/t6200-fmt-merge-msg.sh
t/t6302-for-each-ref-filter.sh
t/t6423-merge-rename-directories.sh
t/t7006-pager.sh
t/t7101-reset-empty-subdirs.sh
t/t7102-reset.sh
t/t7201-co.sh
t/t7518-ident-corner-cases.sh
t/t7900-maintenance.sh
t/t9304-fast-import-marks.sh [new file with mode: 0755]
t/t9801-git-p4-branch.sh
t/t9832-unshelve.sh
t/t9902-completion.sh
t/test-lib-functions.sh
t/test-lib.sh
templates/hooks--push-to-checkout.sample [new file with mode: 0755]
transport-helper.c
transport.c
transport.h
usage.c
userdiff.c
xdiff/xdiff.h
xdiff/xdiffi.c
xdiff/xhistogram.c
xdiff/xpatience.c

diff --git a/.github/workflows/check-whitespace.yml b/.github/workflows/check-whitespace.yml
new file mode 100644 (file)
index 0000000..9d070b9
--- /dev/null
@@ -0,0 +1,69 @@
+name: check-whitespace
+
+# Get the repo with the commits(+1) in the series.
+# Process `git log --check` output to extract just the check errors.
+# Add a comment to the pull request with the check errors.
+
+on:
+  pull_request:
+    types: [opened, synchronize]
+
+jobs:
+  check-whitespace:
+    runs-on: ubuntu-latest
+    steps:
+    - name: Set commit count
+      shell: bash
+      run: echo "::set-env name=COMMIT_DEPTH::$((1+$COMMITS))"
+      env:
+        COMMITS: ${{ github.event.pull_request.commits }}
+
+    - uses: actions/checkout@v2
+      with:
+        fetch-depth: ${{ env.COMMIT_DEPTH }}
+
+    - name: git log --check
+      id: check_out
+      run: |
+        log=
+        commit=
+        while read dash etc
+        do
+          case "${dash}" in
+          "---")
+            commit="${etc}"
+            ;;
+          "")
+            ;;
+          *)
+            if test -n "${commit}"
+            then
+              log="${log}\n${commit}"
+              echo ""
+              echo "--- ${commit}"
+            fi
+            commit=
+            log="${log}\n${dash} ${etc}"
+            echo "${dash} ${etc}"
+            ;;
+          esac
+        done <<< $(git log --check --pretty=format:"---% h% s" -${{github.event.pull_request.commits}})
+
+        if test -n "${log}"
+        then
+          echo "::set-output name=checkout::"${log}""
+          exit 2
+        fi
+
+    - name: Add Check Output as Comment
+      uses: actions/github-script@v3
+      id: add-comment
+      with:
+        script: |
+            github.issues.createComment({
+              issue_number: context.issue.number,
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              body: "Whitespace errors found in workflow ${{ github.workflow }}:\n\n${{ steps.check_out.outputs.checkout }}"
+            })
+      if: ${{ failure() }}
index a940997f1b660c412d24ca54f15571a4af6d38ff..6c3453a10bfefea4b72ce16da02755c5cfe097dc 100644 (file)
@@ -41,35 +41,39 @@ jobs:
         with:
           github-token: ${{secrets.GITHUB_TOKEN}}
           script: |
-            // Figure out workflow ID, commit and tree
-            const { data: run } = await github.actions.getWorkflowRun({
-              owner: context.repo.owner,
-              repo: context.repo.repo,
-              run_id: context.runId,
-            });
-            const workflow_id = run.workflow_id;
-            const head_sha = run.head_sha;
-            const tree_id = run.head_commit.tree_id;
+            try {
+              // Figure out workflow ID, commit and tree
+              const { data: run } = await github.actions.getWorkflowRun({
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                run_id: context.runId,
+              });
+              const workflow_id = run.workflow_id;
+              const head_sha = run.head_sha;
+              const tree_id = run.head_commit.tree_id;
 
-            // See whether there is a successful run for that commit or tree
-            const { data: runs } = await github.actions.listWorkflowRuns({
-              owner: context.repo.owner,
-              repo: context.repo.repo,
-              per_page: 500,
-              status: 'success',
-              workflow_id,
-            });
-            for (const run of runs.workflow_runs) {
-              if (head_sha === run.head_sha) {
-                core.warning(`Successful run for the commit ${head_sha}: ${run.html_url}`);
-                core.setOutput('enabled', ' but skip');
-                break;
-              }
-              if (tree_id === run.head_commit.tree_id) {
-                core.warning(`Successful run for the tree ${tree_id}: ${run.html_url}`);
-                core.setOutput('enabled', ' but skip');
-                break;
+              // See whether there is a successful run for that commit or tree
+              const { data: runs } = await github.actions.listWorkflowRuns({
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                per_page: 500,
+                status: 'success',
+                workflow_id,
+              });
+              for (const run of runs.workflow_runs) {
+                if (head_sha === run.head_sha) {
+                  core.warning(`Successful run for the commit ${head_sha}: ${run.html_url}`);
+                  core.setOutput('enabled', ' but skip');
+                  break;
+                }
+                if (run.head_commit && tree_id === run.head_commit.tree_id) {
+                  core.warning(`Successful run for the tree ${tree_id}: ${run.html_url}`);
+                  core.setOutput('enabled', ' but skip');
+                  break;
+                }
               }
+            } catch (e) {
+              core.warning(e);
             }
 
   windows-build:
index 4f85a089ef9d99e868dd1550992ac234e13266ff..60eed5edcd798343ee4e5d8a28aee1ba0b453059 100644 (file)
@@ -249,7 +249,7 @@ component you're working on, followed by a blank line (always required) and then
 the body of your commit message, which should provide the bulk of the context.
 Remember to be explicit and provide the "Why" of your change, especially if it
 couldn't easily be understood from your diff. When editing your commit message,
-don't remove the Signed-off-by line which was added by `-s` above.
+don't remove the `Signed-off-by` trailer which was added by `-s` above.
 
 ----
 psuh: add a built-in by popular demand
@@ -507,6 +507,9 @@ documentation is consistent with other Git and UNIX manpages; this makes life
 easier for your user, who can skip to the section they know contains the
 information they need.
 
+NOTE: Before trying to build the docs, make sure you have the package `asciidoc`
+installed.
+
 Now that you've written your manpage, you'll need to build it explicitly. We
 convert your AsciiDoc to troff which is man-readable like so:
 
@@ -522,8 +525,6 @@ $ make -C Documentation/ git-psuh.1
 $ man Documentation/git-psuh.1
 ----
 
-NOTE: You may need to install the package `asciidoc` to get this to work.
-
 While this isn't as satisfying as running through `git help`, you can at least
 check that your help page looks right.
 
diff --git a/Documentation/RelNotes/2.29.2.txt b/Documentation/RelNotes/2.29.2.txt
new file mode 100644 (file)
index 0000000..632b5b5
--- /dev/null
@@ -0,0 +1,12 @@
+Git v2.29.2 Release Notes
+=========================
+
+This release is primarily to fix brown-paper-bag breakages in the
+2.29.0 release.
+
+Fixes since v2.29.1
+-------------------
+
+ * In 2.29, "--committer-date-is-author-date" option of "rebase" and
+   "am" subcommands lost the e-mail address by mistake, which has been
+   corrected.
index d6ad811af28743966e231051cb925ba13567bb4a..cfceb1f6cc27649138d40e4cb8af502f15a9fc62 100644 (file)
@@ -6,9 +6,68 @@ Updates since v2.29
 
 UI, Workflows & Features
 
+ * Userdiff for PHP update.
+
+ * Userdiff for Rust update.
+
+ * Userdiff for CSS update.
+
+ * The command line completion script (in contrib/) learned that "git
+   stash show" takes the options "git diff" takes.
+
+ * "git worktree list" now shows if each worktree is locked.  This
+   possibly may open us to show other kinds of states in the future.
+
+ * "git maintenance", an extended big brother of "git gc", continues
+   to evolve.
+
+ * "git push --force-with-lease[=<ref>]" can easily be misused to lose
+   commits unless the user takes good care of their own "git fetch".
+   A new option "--force-if-includes" attempts to ensure that what is
+   being force-pushed was created after examining the commit at the
+   tip of the remote ref that is about to be force-replaced.
+
+ * "git clone" learned clone.defaultremotename configuration variable
+   to customize what nickname to use to call the remote the repository
+   was cloned from.
+
+ * "git checkout" learned to use checkout.guess configuration variable
+   and enable/disable its "--[no-]guess" option accordingly.
+
+ * "git resurrect" script (in contrib/) learned that the object names
+   may be longer than 40-hex depending on the hash function in use.
+
+ * "git diff A...B" learned "git diff --merge-base A B", which is a
+   longer short-hand to say the same thing.
+
+ * A sample 'push-to-checkout' hook, that performs the same as
+   what the built-in default action does, has been added.
+
+ * "git diff" family of commands learned the "-I<regex>" option to
+   ignore hunks whose changed lines all match the given pattern.
+
+ * The userdiff pattern learned to identify the function definition in
+   POSIX shells and bash.
+
 
 Performance, Internal Implementation, Development Support etc.
 
+ * Use "git archive" more to produce the release tarball.
+
+ * GitHub Actions automated test improvement to skip tests on a tree
+   identical to what has already been tested.
+
+ * Test-coverage for running commit-graph task "git maintenance" has
+   been extended.
+
+ * Our test scripts can be told to run only individual pieces while
+   skipping others with the "--run=..." option; they were taught to
+   take a substring of test title, in addition to numbers, to name the
+   test pieces to run.
+
+ * Adjust tests so that they won't scream when the default initial
+   branch name is changed to 'main'.
+
 
 Fixes since v2.29
 -----------------
@@ -18,5 +77,69 @@ Fixes since v2.29
    corrected.
    (merge 5f35edd9d7 jk/committer-date-is-author-date-fix later to maint).
 
+ * "git checkout -p A...B [-- <path>]" did not work, even though the
+   same command without "-p" correctly used the merge-base between
+   commits A and B.
+   (merge 35166b1fb5 dl/checkout-p-merge-base later to maint).
+
+ * The side-band status report can be sent at the same time as the
+   primary payload multiplexed, but the demultiplexer on the receiving
+   end incorrectly split a single status report into two, which has
+   been corrected.
+   (merge 712b0377db js/avoid-split-sideband-message later to maint).
+
+ * "git fast-import" wasted a lot of memory when many marks were in use.
+   (merge 3f018ec716 jk/fast-import-marks-alloc-fix later to maint).
+
+ * A test helper "test_cmp A B" was taught to diagnose missing files A
+   or B as a bug in test, but some tests legitimately wanted to notice
+   a failure to even create file B as an error, in addition to leaving
+   the expected result in it, and were misdiagnosed as a bug.  This
+   has been corrected.
+   (merge 262d5ad5a5 es/test-cmp-typocatcher later to maint).
+
+ * When "git commit-graph" detects the same commit recorded more than
+   once while it is merging the layers, it used to die.  The code now
+   ignores all but one of them and continues.
+   (merge 85102ac71b ds/commit-graph-merging-fix later to maint).
+
+ * The meaning of a Signed-off-by trailer can vary from project to
+   project; this and also what it means to this project has been
+   clarified in the documentation.
+   (merge 3abd4a67d9 bk/sob-dco later to maint).
+
+ * "git credential' didn't honor the core.askPass configuration
+   variable (among other things), which has been corrected.
+   (merge 567ad2c0f9 tk/credential-config later to maint).
+
+ * Dev support to catch a tentative definition of a variable in our C
+   code as an error.
+   (merge 5539183622 jk/no-common later to maint).
+
+ * "git rebase --rebase-merges" did not correctly pass --gpg-sign
+   command line option to underlying "git merge" when replaying a merge
+   using non-default merge strategy or when replaying an octopus merge
+   (because replaying a two-head merge with the default strategy was
+   done in a separate codepath, the problem did not trigger for most
+   users), which has been corrected.
+   (merge 43ad4f2eca sc/sequencer-gpg-octopus later to maint).
+
+ * "git apply -R" did not handle patches that touch the same path
+   twice correctly, which has been corrected.  This is most relevant
+   in a patch that changes a path from a regular file to a symbolic
+   link (and vice versa).
+   (merge b0f266de11 jt/apply-reverse-twice later to maint).
+
+ * A recent oid->hash conversion missed one spot, breaking "git svn".
+   (merge 03bb366de4 bc/svn-hash-oid-fix later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge 3e0a5dc9af cc/doc-filter-branch-typofix later to maint).
+   (merge 32c83afc2c cw/ci-ghwf-check-ws-errors later to maint).
+   (merge 5eb2ed691b rs/tighten-callers-of-deref-tag later to maint).
+   (merge 6db29ab213 jk/fast-import-marks-cleanup later to maint).
+   (merge e5cf6d3df4 nk/dir-c-comment-update later to maint).
+   (merge 5710dcce74 jk/report-fn-typedef later to maint).
+   (merge 9a82db1056 en/sequencer-rollback-lock-cleanup later to maint).
+   (merge 4e1bee9a99 js/t7006-cleanup later to maint).
+   (merge f5bcde6c58 es/tutorial-mention-asciidoc-early later to maint).
index 291b61e2621321e30277cd426fc842982c845ca6..d12094bac5f46a3d8df1aee0b380e563a51dad47 100644 (file)
@@ -209,7 +209,7 @@ send them as replies to either an additional "cover letter" message
 (see below), the first patch, or the respective preceding patch.
 
 If your log message (including your name on the
-Signed-off-by line) is not writable in ASCII, make sure that
+`Signed-off-by` trailer) is not writable in ASCII, make sure that
 you send off a message in the correct encoding.
 
 WARNING: Be wary of your MUAs word-wrap
@@ -229,7 +229,7 @@ previously sent.
 The `git format-patch` command follows the best current practice to
 format the body of an e-mail message.  At the beginning of the
 patch should come your commit message, ending with the
-Signed-off-by: lines, and a line that consists of three dashes,
+`Signed-off-by` trailers, and a line that consists of three dashes,
 followed by the diffstat information and the patch itself.  If
 you are forwarding a patch from somebody else, optionally, at
 the beginning of the e-mail message just before the commit
@@ -290,25 +290,24 @@ identify them), to solicit comments and reviews.
 :git-ml: footnote:[The mailing list: git@vger.kernel.org]
 
 After the list reached a consensus that it is a good idea to apply the
-patch, re-send it with "To:" set to the maintainer{current-maintainer} and "cc:" the
-list{git-ml} for inclusion.
+patch, re-send it with "To:" set to the maintainer{current-maintainer}
+and "cc:" the list{git-ml} for inclusion.  This is especially relevant
+when the maintainer did not heavily participate in the discussion and
+instead left the review to trusted others.
 
 Do not forget to add trailers such as `Acked-by:`, `Reviewed-by:` and
 `Tested-by:` lines as necessary to credit people who helped your
-patch.
+patch, and "cc:" them when sending such a final version for inclusion.
 
 [[sign-off]]
-=== Certify your work by adding your "Signed-off-by: " line
+=== Certify your work by adding your `Signed-off-by` trailer
 
-To improve tracking of who did what, we've borrowed the
-"sign-off" procedure from the Linux kernel project on patches
-that are being emailed around.  Although core Git is a lot
-smaller project it is a good discipline to follow it.
+To improve tracking of who did what, we ask you to certify that you
+wrote the patch or have the right to pass it on under the same license
+as ours, by "signing off" your patch.  Without sign-off, we cannot
+accept your patches.
 
-The sign-off is a simple line at the end of the explanation for
-the patch, which certifies that you wrote it or otherwise have
-the right to pass it on as an open-source patch.  The rules are
-pretty simple: if you can certify the below D-C-O:
+If you can certify the below D-C-O:
 
 [[dco]]
 .Developer's Certificate of Origin 1.1
@@ -338,23 +337,29 @@ d. I understand and agree that this project and the contribution
    this project or the open source license(s) involved.
 ____
 
-then you just add a line saying
+you add a "Signed-off-by" trailer to your commit, that looks like
+this:
 
 ....
        Signed-off-by: Random J Developer <random@developer.example.org>
 ....
 
-This line can be automatically added by Git if you run the git-commit
-command with the -s option.
+This line can be added by Git if you run the git-commit command with
+the -s option.
 
-Notice that you can place your own Signed-off-by: line when
+Notice that you can place your own `Signed-off-by` trailer when
 forwarding somebody else's patch with the above rules for
 D-C-O.  Indeed you are encouraged to do so.  Do not forget to
 place an in-body "From: " line at the beginning to properly attribute
 the change to its true author (see (2) above).
 
+This procedure originally came from the Linux kernel project, so our
+rule is quite similar to theirs, but what exactly it means to sign-off
+your patch differs from project to project, so it may be different
+from that of the project you are accustomed to.
+
 [[real-name]]
-Also notice that a real name is used in the Signed-off-by: line. Please
+Also notice that a real name is used in the `Signed-off-by` trailer. Please
 don't hide your real name.
 
 [[commit-trailers]]
index bf706b950e6615b955fa481853bfbe5b84d2486c..025ca4df1197d1ed107f95f9994f6a998e5b6130 100644 (file)
@@ -334,6 +334,8 @@ include::config/checkout.txt[]
 
 include::config/clean.txt[]
 
+include::config/clone.txt[]
+
 include::config/color.txt[]
 
 include::config/column.txt[]
index bdd37c3eaa3203f504fdc07eeac358ad58887965..acbd0c09aa4fb0b2146ba11f264be55e61e73c77 100644 (file)
@@ -10,9 +10,8 @@ advice.*::
                that the check is disabled.
        pushUpdateRejected::
                Set this variable to 'false' if you want to disable
-               'pushNonFFCurrent',
-               'pushNonFFMatching', 'pushAlreadyExists',
-               'pushFetchFirst', and 'pushNeedsForce'
+               'pushNonFFCurrent', 'pushNonFFMatching', 'pushAlreadyExists',
+               'pushFetchFirst', 'pushNeedsForce', and 'pushRefNeedsUpdate'
                simultaneously.
        pushNonFFCurrent::
                Advice shown when linkgit:git-push[1] fails due to a
@@ -41,6 +40,10 @@ advice.*::
                we can still suggest that the user push to either
                refs/heads/* or refs/tags/* based on the type of the
                source object.
+       pushRefNeedsUpdate::
+               Shown when linkgit:git-push[1] rejects a forced update of
+               a branch when its remote-tracking ref has updates that we
+               do not have locally.
        statusAheadBehind::
                Shown when linkgit:git-status[1] computes the ahead/behind
                counts for a local ref compared to its remote tracking ref,
index 6b646813abadc95a969d5c20f6c4350a284bf129..2cddf7b4b40213155a09e85e6499508fc4a30c86 100644 (file)
@@ -1,18 +1,23 @@
 checkout.defaultRemote::
-       When you run 'git checkout <something>'
-       or 'git switch <something>' and only have one
+       When you run `git checkout <something>`
+       or `git switch <something>` and only have one
        remote, it may implicitly fall back on checking out and
-       tracking e.g. 'origin/<something>'. This stops working as soon
-       as you have more than one remote with a '<something>'
+       tracking e.g. `origin/<something>`. This stops working as soon
+       as you have more than one remote with a `<something>`
        reference. This setting allows for setting the name of a
        preferred remote that should always win when it comes to
        disambiguation. The typical use-case is to set this to
        `origin`.
 +
 Currently this is used by linkgit:git-switch[1] and
-linkgit:git-checkout[1] when 'git checkout <something>'
-or 'git switch <something>'
-will checkout the '<something>' branch on another remote,
-and by linkgit:git-worktree[1] when 'git worktree add' refers to a
+linkgit:git-checkout[1] when `git checkout <something>`
+or `git switch <something>`
+will checkout the `<something>` branch on another remote,
+and by linkgit:git-worktree[1] when `git worktree add` refers to a
 remote branch. This setting might be used for other checkout-like
 commands or functionality in the future.
+
+checkout.guess::
+       Provides the default value for the `--guess` or `--no-guess`
+       option in `git checkout` and `git switch`. See
+       linkgit:git-switch[1] and linkgit:git-checkout[1].
diff --git a/Documentation/config/clone.txt b/Documentation/config/clone.txt
new file mode 100644 (file)
index 0000000..47de36a
--- /dev/null
@@ -0,0 +1,4 @@
+clone.defaultRemoteName::
+       The name of the remote to create when cloning a repository.  Defaults to
+       `origin`, and can be overridden by passing the `--origin` command-line
+       option to linkgit:git-clone[1].
index 02002cf109e90ecfaf80aba698f235111bd4e05e..160aacad84bae60ff69ea73b9edbb63bee9c2b56 100644 (file)
@@ -606,8 +606,8 @@ core.useReplaceRefs::
 
 core.multiPackIndex::
        Use the multi-pack-index file to track multiple packfiles using a
-       single index. See link:technical/multi-pack-index.html[the
-       multi-pack-index design document].
+       single index. See linkgit:git-multi-pack-index[1] for more
+       information. Defaults to true.
 
 core.sparseCheckout::
        Enable "sparse checkout" feature. See linkgit:git-sparse-checkout[1]
index c2efd8758a5841d40b41003edb7713089b0650a3..851bf410a3bba8ff41e78993b9642d7a23d1652a 100644 (file)
@@ -79,7 +79,7 @@ format.thread::
 
 format.signOff::
        A boolean value which lets you enable the `-s/--signoff` option of
-       format-patch by default. *Note:* Adding the Signed-off-by: line to a
+       format-patch by default. *Note:* Adding the `Signed-off-by` trailer to a
        patch should be a conscious act and means that you certify you have
        the rights to submit this work under the same open source license.
        Please see the 'SubmittingPatches' document for further discussion.
index 7cc6700d574d3208178d7d36e00d62f71d5b10bf..a0706d8f098becab7b5df71c77f718b03cc9c1a2 100644 (file)
@@ -14,3 +14,21 @@ maintenance.commit-graph.auto::
        reachable commits that are not in the commit-graph file is at least
        the value of `maintenance.commit-graph.auto`. The default value is
        100.
+
+maintenance.loose-objects.auto::
+       This integer config option controls how often the `loose-objects` task
+       should be run as part of `git maintenance run --auto`. If zero, then
+       the `loose-objects` task will not run with the `--auto` option. A
+       negative value will force the task to run every time. Otherwise, a
+       positive value implies the command should run when the number of
+       loose objects is at least the value of `maintenance.loose-objects.auto`.
+       The default value is 100.
+
+maintenance.incremental-repack.auto::
+       This integer config option controls how often the `incremental-repack`
+       task should be run as part of `git maintenance run --auto`. If zero,
+       then the `incremental-repack` task will not run with the `--auto`
+       option. A negative value will force the task to run every time.
+       Otherwise, a positive value implies the command should run when the
+       number of pack-files not in the multi-pack-index is at least the value
+       of `maintenance.incremental-repack.auto`. The default value is 10.
index f5e5b38c6889e92d5366e7be1140db57e8f708c4..21b256e0a4ec8af1dd1227ec2408ff924f988233 100644 (file)
@@ -114,3 +114,9 @@ push.recurseSubmodules::
        specifying '--recurse-submodules=check|on-demand|no'.
        If not set, 'no' is used by default, unless 'submodule.recurse' is
        set (in which case a 'true' value means 'on-demand').
+
+push.useForceIfIncludes::
+       If set to "true", it is equivalent to specifying
+       `--force-if-includes` as an option to linkgit:git-push[1]
+       in the command line. Adding `--no-force-if-includes` at the
+       time of push overrides this configuration setting.
index 573fb9bb71e2b7df1062258289a0af308c50bf5b..ee52b65e4613b7dedf76ddb5278e87dbeed36c59 100644 (file)
@@ -687,6 +687,11 @@ endif::git-format-patch[]
 --ignore-blank-lines::
        Ignore changes whose lines are all blank.
 
+-I<regex>::
+--ignore-matching-lines=<regex>::
+       Ignore changes whose all lines match <regex>.  This option may
+       be specified more than once.
+
 --inter-hunk-context=<lines>::
        Show the context between diff hunks, up to the specified number
        of lines, thereby fusing hunks that are close to each other.
index 38c0852139c17077aacb6bc43fcf8102cc6570f4..06bc063542f3044cc9fe7ced6ecd4922d2e209e2 100644 (file)
@@ -33,7 +33,7 @@ OPTIONS
 
 -s::
 --signoff::
-       Add a `Signed-off-by:` line to the commit message, using
+       Add a `Signed-off-by` trailer to the commit message, using
        the committer identity of yourself.
        See the signoff option in linkgit:git-commit[1] for more information.
 
index afa5c11fd3c4d58f978e5bb99a03517c642584c6..b1a6fe4499730690777de66558621446e1e58bac 100644 (file)
@@ -192,7 +192,10 @@ branches from there if `<branch>` is ambiguous but exists on the
 'origin' remote. See also `checkout.defaultRemote` in
 linkgit:git-config[1].
 +
-Use `--no-guess` to disable this.
+`--guess` is the default behavior. Use `--no-guess` to disable it.
++
+The default behavior can be set via the `checkout.guess` configuration
+variable.
 
 -l::
        Create the new branch's reflog; see linkgit:git-branch[1] for
@@ -351,6 +354,10 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 <tree-ish>::
        Tree to checkout from (when paths are given). If not specified,
        the index will be used.
++
+As a special case, you may use `"A...B"` as a shortcut for the
+merge base of `A` and `B` if there is exactly one merge base. You can
+leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 \--::
        Do not interpret any more arguments as options.
index 75feeef08a0e64ac3f6bfc35290e92984c1a1580..5d750314b299cc7157034b9bffd6a5aa3afe51d4 100644 (file)
@@ -104,7 +104,7 @@ effect to your index in a row.
 
 -s::
 --signoff::
-       Add Signed-off-by line at the end of the commit message.
+       Add a `Signed-off-by` trailer at the end of the commit message.
        See the signoff option in linkgit:git-commit[1] for more information.
 
 -S[<keyid>]::
index 097e6a86c5bdf31c9b30e277e9659bb3015081af..876aedcd472a4b04ee28f3cc524195a701222db5 100644 (file)
@@ -183,8 +183,9 @@ objects from the source repository into a pack in the cloned repository.
 
 -o <name>::
 --origin <name>::
-       Instead of using the remote name `origin` to keep track
-       of the upstream repository, use `<name>`.
+       Instead of using the remote name `origin` to keep track of the upstream
+       repository, use `<name>`.  Overrides `clone.defaultRemoteName` from the
+       config.
 
 -b <name>::
 --branch <name>::
index de6b6de230223c2e3fd37b55827ba85265c3f052..e1f48c95b3ca37e67af88e634a44db88dd1ca6a9 100644 (file)
@@ -39,7 +39,9 @@ COMMANDS
 --------
 'write'::
 
-Write a commit-graph file based on the commits found in packfiles.
+Write a commit-graph file based on the commits found in packfiles. If
+the config option `core.commitGraph` is disabled, then this command will
+output a warning, then return success without writing a commit-graph file.
 +
 With the `--stdin-packs` option, generate the new commit graph by
 walking objects only in the specified pack-indexes. (Cannot be combined
index a3baea32aedddfa00077adc5f2a2b36e38a5ea26..17150fa7eabe8092acf09f63220b515a0002a7b4 100644 (file)
@@ -59,6 +59,7 @@ commit by giving the same set of parameters (options and paths).
 If you make a commit and then find a mistake immediately after
 that, you can recover from it with 'git reset'.
 
+:git-commit: 1
 
 OPTIONS
 -------
@@ -163,14 +164,7 @@ The `-m` option is mutually exclusive with `-c`, `-C`, and `-F`.
        message, the commit is aborted.  This has no effect when a message
        is given by other means, e.g. with the `-m` or `-F` options.
 
--s::
---signoff::
-       Add Signed-off-by line by the committer at the end of the commit
-       log message.  The meaning of a signoff depends on the project,
-       but it typically certifies that committer has
-       the rights to submit this work under the same license and
-       agrees to a Developer Certificate of Origin
-       (see http://developercertificate.org/ for more information).
+include::signoff-option.txt[]
 
 -n::
 --no-verify::
index f4bd8155c0a707308162e050d5d59b5dbd5ca7a6..27acb31cbf26f6d842a7445324950f6dfc2b2e68 100644 (file)
@@ -9,7 +9,7 @@ git-diff-index - Compare a tree to the working tree or index
 SYNOPSIS
 --------
 [verse]
-'git diff-index' [-m] [--cached] [<common diff options>] <tree-ish> [<path>...]
+'git diff-index' [-m] [--cached] [--merge-base] [<common diff options>] <tree-ish> [<path>...]
 
 DESCRIPTION
 -----------
@@ -27,7 +27,12 @@ include::diff-options.txt[]
        The id of a tree object to diff against.
 
 --cached::
-       do not consider the on-disk file at all
+       Do not consider the on-disk file at all.
+
+--merge-base::
+       Instead of comparing <tree-ish> directly, use the merge base
+       between <tree-ish> and HEAD instead.  <tree-ish> must be a
+       commit.
 
 -m::
        By default, files recorded in the index but not checked
index 5c8a2a5e9755db17be4063cdcb2edb8b48237436..2fc24c542f8cbd3e20af3575b09f52b6a4de5822 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git diff-tree' [--stdin] [-m] [-s] [-v] [--no-commit-id] [--pretty]
-             [-t] [-r] [-c | --cc] [--combined-all-paths] [--root]
+             [-t] [-r] [-c | --cc] [--combined-all-paths] [--root] [--merge-base]
              [<common diff options>] <tree-ish> [<tree-ish>] [<path>...]
 
 DESCRIPTION
@@ -43,6 +43,11 @@ include::diff-options.txt[]
        When `--root` is specified the initial commit will be shown as a big
        creation event. This is equivalent to a diff against the NULL tree.
 
+--merge-base::
+       Instead of comparing the <tree-ish>s directly, use the merge
+       base between the two <tree-ish>s as the "before" side.  There
+       must be two <tree-ish>s given and they must both be commits.
+
 --stdin::
        When `--stdin` is specified, the command does not take
        <tree-ish> arguments from the command line.  Instead, it
index 727f24d16ec931e4bb23f33f4f44f5c341e497f0..7f4c8a8ce7fd54f87e2c3936a6a1cf1cc623c60d 100644 (file)
@@ -10,8 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git diff' [<options>] [<commit>] [--] [<path>...]
-'git diff' [<options>] --cached [<commit>] [--] [<path>...]
-'git diff' [<options>] <commit> [<commit>...] <commit> [--] [<path>...]
+'git diff' [<options>] --cached [--merge-base] [<commit>] [--] [<path>...]
+'git diff' [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...]
 'git diff' [<options>] <commit>...<commit> [--] [<path>...]
 'git diff' [<options>] <blob> <blob>
 'git diff' [<options>] --no-index [--] <path> <path>
@@ -40,7 +40,7 @@ files on disk.
        or when running the command outside a working tree
        controlled by Git. This form implies `--exit-code`.
 
-'git diff' [<options>] --cached [<commit>] [--] [<path>...]::
+'git diff' [<options>] --cached [--merge-base] [<commit>] [--] [<path>...]::
 
        This form is to view the changes you staged for the next
        commit relative to the named <commit>.  Typically you
@@ -49,6 +49,10 @@ files on disk.
        If HEAD does not exist (e.g. unborn branches) and
        <commit> is not given, it shows all staged changes.
        --staged is a synonym of --cached.
++
+If --merge-base is given, instead of using <commit>, use the merge base
+of <commit> and HEAD.  `git diff --merge-base A` is equivalent to
+`git diff $(git merge-base A HEAD)`.
 
 'git diff' [<options>] <commit> [--] [<path>...]::
 
@@ -58,23 +62,27 @@ files on disk.
        branch name to compare with the tip of a different
        branch.
 
-'git diff' [<options>] <commit> <commit> [--] [<path>...]::
+'git diff' [<options>] [--merge-base] <commit> <commit> [--] [<path>...]::
 
        This is to view the changes between two arbitrary
        <commit>.
++
+If --merge-base is given, use the merge base of the two commits for the
+"before" side.  `git diff --merge-base A B` is equivalent to
+`git diff $(git merge-base A B) B`.
 
 'git diff' [<options>] <commit> <commit>... <commit> [--] [<path>...]::
 
        This form is to view the results of a merge commit.  The first
        listed <commit> must be the merge itself; the remaining two or
        more commits should be its parents.  A convenient way to produce
-       the desired set of revisions is to use the {caret}@ suffix.
+       the desired set of revisions is to use the `^@` suffix.
        For instance, if `master` names a merge commit, `git diff master
        master^@` gives the same combined diff as `git show master`.
 
 'git diff' [<options>] <commit>..<commit> [--] [<path>...]::
 
-       This is synonymous to the earlier form (without the "..") for
+       This is synonymous to the earlier form (without the `..`) for
        viewing the changes between two arbitrary <commit>.  If <commit> on
        one side is omitted, it will have the same effect as
        using HEAD instead.
@@ -83,20 +91,20 @@ files on disk.
 
        This form is to view the changes on the branch containing
        and up to the second <commit>, starting at a common ancestor
-       of both <commit>.  "git diff A\...B" is equivalent to
-       "git diff $(git merge-base A B) B".  You can omit any one
+       of both <commit>.  `git diff A...B` is equivalent to
+       `git diff $(git merge-base A B) B`.  You can omit any one
        of <commit>, which has the same effect as using HEAD instead.
 
 Just in case you are doing something exotic, it should be
 noted that all of the <commit> in the above description, except
-in the last two forms that use ".." notations, can be any
-<tree>.
+in the `--merge-base` case and in the last two forms that use `..`
+notations, can be any <tree>.
 
 For a more complete list of ways to spell <commit>, see
 "SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
 However, "diff" is about comparing two _endpoints_, not ranges,
-and the range notations ("<commit>..<commit>" and
-"<commit>\...<commit>") do not mean a range as defined in the
+and the range notations (`<commit>..<commit>` and
+`<commit>...<commit>`) do not mean a range as defined in the
 "SPECIFYING RANGES" section in linkgit:gitrevisions[7].
 
 'git diff' [<options>] <blob> <blob>::
@@ -144,9 +152,9 @@ $ git diff HEAD       <3>
 +
 <1> Changes in the working tree not yet staged for the next commit.
 <2> Changes between the index and your last commit; what you
-    would be committing if you run "git commit" without "-a" option.
+    would be committing if you run `git commit` without `-a` option.
 <3> Changes in the working tree since your last commit; what you
-    would be committing if you run "git commit -a"
+    would be committing if you run `git commit -a`
 
 Comparing with arbitrary commits::
 +
index 0f81d0437bb65bd0ee68ef63edf7bb48d9ea87a6..bf1bb40f6303fab2a8362af493b1839af21471f2 100644 (file)
@@ -119,7 +119,7 @@ include::diff-options.txt[]
 
 -s::
 --signoff::
-       Add `Signed-off-by:` line to the commit message, using
+       Add a `Signed-off-by` trailer to the commit message, using
        the committer identity of yourself.
        See the signoff option in linkgit:git-commit[1] for more information.
 
index 6abcb8255a2bfdc908ac03960aece0cd1835a8fd..3f5d8946b4d9ba25c39c21dbfe20f8ef64c8f983 100644 (file)
@@ -47,6 +47,21 @@ commit-graph::
        `commit-graph-chain` file. They will be deleted by a later run based
        on the expiration delay.
 
+prefetch::
+       The `prefetch` task updates the object directory with the latest
+       objects from all registered remotes. For each remote, a `git fetch`
+       command is run. The refmap is custom to avoid updating local or remote
+       branches (those in `refs/heads` or `refs/remotes`). Instead, the
+       remote refs are stored in `refs/prefetch/<remote>/`. Also, tags are
+       not updated.
++
+This is done to avoid disrupting the remote-tracking branches. The end users
+expect these refs to stay unmoved unless they initiate a fetch.  With prefetch
+task, however, the objects necessary to complete a later real fetch would
+already be obtained, so the real fetch would go faster.  In the ideal case,
+it will just become an update to bunch of remote-tracking branches without
+any object transfer.
+
 gc::
        Clean up unnecessary files and optimize the local repository. "GC"
        stands for "garbage collection," but this task performs many
@@ -55,6 +70,39 @@ gc::
        be disruptive in some situations, as it deletes stale data. See
        linkgit:git-gc[1] for more details on garbage collection in Git.
 
+loose-objects::
+       The `loose-objects` job cleans up loose objects and places them into
+       pack-files. In order to prevent race conditions with concurrent Git
+       commands, it follows a two-step process. First, it deletes any loose
+       objects that already exist in a pack-file; concurrent Git processes
+       will examine the pack-file for the object data instead of the loose
+       object. Second, it creates a new pack-file (starting with "loose-")
+       containing a batch of loose objects. The batch size is limited to 50
+       thousand objects to prevent the job from taking too long on a
+       repository with many loose objects. The `gc` task writes unreachable
+       objects as loose objects to be cleaned up by a later step only if
+       they are not re-added to a pack-file; for this reason it is not
+       advisable to enable both the `loose-objects` and `gc` tasks at the
+       same time.
+
+incremental-repack::
+       The `incremental-repack` job repacks the object directory
+       using the `multi-pack-index` feature. In order to prevent race
+       conditions with concurrent Git commands, it follows a two-step
+       process. First, it calls `git multi-pack-index expire` to delete
+       pack-files unreferenced by the `multi-pack-index` file. Second, it
+       calls `git multi-pack-index repack` to select several small
+       pack-files and repack them into a bigger one, and then update the
+       `multi-pack-index` entries that refer to the small pack-files to
+       refer to the new pack-file. This prepares those small pack-files
+       for deletion upon the next run of `git multi-pack-index expire`.
+       The selection of the small pack-files is such that the expected
+       size of the big pack-file is at least the batch size; see the
+       `--batch-size` option for the `repack` subcommand in
+       linkgit:git-multi-pack-index[1]. The default batch-size is zero,
+       which is a special case that attempts to repack all pack-files
+       into a single pack-file.
+
 OPTIONS
 -------
 --auto::
index 3b8053447e204499f28ab1616ce74efa87524536..ab103c82cfdc38c37e7aa0e07c324ca31681ae22 100644 (file)
@@ -13,7 +13,7 @@ SYNOPSIS
           [--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose]
           [-u | --set-upstream] [-o <string> | --push-option=<string>]
           [--[no-]signed|--signed=(true|false|if-asked)]
-          [--force-with-lease[=<refname>[:<expect>]]]
+          [--force-with-lease[=<refname>[:<expect>]] [--force-if-includes]]
           [--no-verify] [<repository> [<refspec>...]]
 
 DESCRIPTION
@@ -320,6 +320,14 @@ seen and are willing to overwrite, then rewrite history, and finally
 force push changes to `master` if the remote version is still at
 `base`, regardless of what your local `remotes/origin/master` has been
 updated to in the background.
++
+Alternatively, specifying `--force-if-includes` as an ancillary option
+along with `--force-with-lease[=<refname>]` (i.e., without saying what
+exact commit the ref on the remote side must be pointing at, or which
+refs on the remote side are being protected) at the time of "push" will
+verify if updates from the remote-tracking refs that may have been
+implicitly updated in the background are integrated locally before
+allowing a forced update.
 
 -f::
 --force::
@@ -341,6 +349,22 @@ one branch, use a `+` in front of the refspec to push (e.g `git push
 origin +master` to force a push to the `master` branch). See the
 `<refspec>...` section above for details.
 
+--[no-]force-if-includes::
+       Force an update only if the tip of the remote-tracking ref
+       has been integrated locally.
++
+This option enables a check that verifies if the tip of the
+remote-tracking ref is reachable from one of the "reflog" entries of
+the local branch based in it for a rewrite. The check ensures that any
+updates from the remote have been incorporated locally by rejecting the
+forced update if that is not the case.
++
+If the option is passed without specifying `--force-with-lease`, or
+specified along with `--force-with-lease=<refname>:<expect>`, it is
+a "no-op".
++
+Specifying `--no-force-if-includes` disables this behavior.
+
 --repo=<repository>::
        This option is equivalent to the <repository> argument. If both
        are specified, the command-line argument takes precedence.
index 38e15488f651718bf220453504a39fb8ef0800f7..a0487b5cc58816ac8c4ec700e56a5d116fd61824 100644 (file)
@@ -496,7 +496,7 @@ See also INCOMPATIBLE OPTIONS below.
 See also INCOMPATIBLE OPTIONS below.
 
 --signoff::
-       Add a Signed-off-by: trailer to all the rebased commits. Note
+       Add a `Signed-off-by` trailer to all the rebased commits. Note
        that if `--interactive` is given then only commits marked to be
        picked, edited or reworded will have the trailer added.
 +
index ea73386c811b5fd96ba44cc09e7f6992cbbd747e..31c29c9b31b202eeba1fbb2fe0d31054d46d4882 100644 (file)
@@ -203,6 +203,17 @@ The remote configuration is achieved using the `remote.origin.url` and
 `remote.origin.fetch` configuration variables.  (See
 linkgit:git-config[1]).
 
+EXIT STATUS
+-----------
+
+On success, the exit status is `0`.
+
+When subcommands such as 'add', 'rename', and 'remove' can't find the
+remote in question, the exit status is `2`. When the remote already
+exists, the exit status is `3`.
+
+On any other error, the exit status may be any other non-zero value.
+
 EXAMPLES
 --------
 
index 84c6c400109851ac34185a4f9004aada07aa79aa..55bde91ef9e54be6c9ecf928f6f72c775b45809a 100644 (file)
@@ -40,6 +40,10 @@ OPTIONS
 +
 If not specified, the contents are restored from `HEAD` if `--staged` is
 given, otherwise from the index.
++
+As a special case, you may use `"A...B"` as a shortcut for the
+merge base of `A` and `B` if there is exactly one merge base. You can
+leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 -p::
 --patch::
index 044276e9da62af44af9a8d897df0dd536556610b..bb92a4a4516d2a93a6e85facb0831aabc1be126c 100644 (file)
@@ -99,7 +99,7 @@ effect to your index in a row.
 
 -s::
 --signoff::
-       Add Signed-off-by line at the end of the commit message.
+       Add a `Signed-off-by` trailer at the end of the commit message.
        See the signoff option in linkgit:git-commit[1] for more information.
 
 --strategy=<strategy>::
index 0a69810147e451d7cac290020f009631b1da9e46..b7bbbeadef6df43a9497cbbfad1f8cbf8213cd6f 100644 (file)
@@ -313,7 +313,7 @@ Automating
        the value of `sendemail.identity`.
 
 --[no-]signed-off-by-cc::
-       If this is set, add emails found in Signed-off-by: or Cc: lines to the
+       If this is set, add emails found in the `Signed-off-by` trailer or Cc: lines to the
        cc list. Default is the value of `sendemail.signedoffbycc` configuration
        value; if that is unspecified, default to --signed-off-by-cc.
 
@@ -340,7 +340,7 @@ Automating
   except for self (use 'self' for that).
 - 'bodycc' will avoid including anyone mentioned in Cc lines in the
   patch body (commit message) except for self (use 'self' for that).
-- 'sob' will avoid including anyone mentioned in Signed-off-by lines except
+- 'sob' will avoid including anyone mentioned in the Signed-off-by trailers except
   for self (use 'self' for that).
 - 'misc-by' will avoid including anyone mentioned in Acked-by,
   Reviewed-by, Tested-by and other "-by" lines in the patch body,
index 6624a14fbd70992a0bfbba99b73cc2110208c421..67b143cc81ea9534722a082fdfbc016081621411 100644 (file)
@@ -701,7 +701,7 @@ creating the branch or tag.
 
 --use-log-author::
        When retrieving svn commits into Git (as part of 'fetch', 'rebase', or
-       'dcommit' operations), look for the first `From:` or `Signed-off-by:` line
+       'dcommit' operations), look for the first `From:` line or `Signed-off-by` trailer
        in the log message and use that as the author string.
 +
 [verse]
@@ -710,7 +710,7 @@ config key: svn.useLogAuthor
 --add-author-from::
        When committing to svn from Git (as part of 'set-tree' or 'dcommit'
        operations), if the existing log message doesn't already have a
-       `From:` or `Signed-off-by:` line, append a `From:` line based on the
+       `From:` or `Signed-off-by` trailer, append a `From:` line based on the
        Git commit's author string.  If you use this, then `--use-log-author`
        will retrieve a valid author string for all commits.
 +
index 3759c3a265b5b9761a9df4d8e6b9d733d29aee04..5c438cd505875841763f0151dfe0a2c1454dfcc5 100644 (file)
@@ -103,6 +103,9 @@ ambiguous but exists on the 'origin' remote. See also
 `checkout.defaultRemote` in linkgit:git-config[1].
 +
 `--guess` is the default behavior. Use `--no-guess` to disable it.
++
+The default behavior can be set via the `checkout.guess` configuration
+variable.
 
 -f::
 --force::
index 32e8440cdea5751e6f699fff62ba2d7675057cae..af06128cc9e656de17a3f2b9180ca14135f0418c 100644 (file)
@@ -96,8 +96,9 @@ list::
 
 List details of each working tree.  The main working tree is listed first,
 followed by each of the linked working trees.  The output details include
-whether the working tree is bare, the revision currently checked out, and the
-branch currently checked out (or "detached HEAD" if none).
+whether the working tree is bare, the revision currently checked out, the
+branch currently checked out (or "detached HEAD" if none), and "locked" if
+the worktree is locked.
 
 lock::
 
index 2d0a03715be65edfc4c8be6ca8f2a959ddde7540..e84e104f9325526bad7dca6043e897ad21b41906 100644 (file)
@@ -802,6 +802,9 @@ patterns are available:
 
 - `ada` suitable for source code in the Ada language.
 
+- `bash` suitable for source code in the Bourne-Again SHell language.
+  Covers a superset of POSIX shell function definitions.
+
 - `bibtex` suitable for files with BibTeX coded references.
 
 - `cpp` suitable for source code in the C and C++ languages.
index 6e461ace6e6434789cee58fb95542e4fc1653d78..4e097dc4e9de8ee0c3a6029914e3816f0f32b31c 100644 (file)
@@ -164,7 +164,7 @@ can also be used to refuse the commit after inspecting the message
 file.
 
 The default 'commit-msg' hook, when enabled, detects duplicate
-"Signed-off-by" lines, and aborts the commit if one is found.
+`Signed-off-by` trailers, and aborts the commit if one is found.
 
 post-commit
 ~~~~~~~~~~~
index 80d4831662c5e5fee9cdcc37e14929ce94f9b81a..eb0aabd396ff924ea30dc6f81bc1382a061a0b3d 100644 (file)
@@ -77,16 +77,7 @@ When not possible, refuse to merge and exit with a non-zero status.
 With --no-log do not list one-line descriptions from the
 actual commits being merged.
 
---signoff::
---no-signoff::
-       Add Signed-off-by line by the committer at the end of the commit
-       log message.  The meaning of a signoff depends on the project,
-       but it typically certifies that committer has
-       the rights to submit this work under the same license and
-       agrees to a Developer Certificate of Origin
-       (see http://developercertificate.org/ for more information).
-+
-With --no-signoff do not add a Signed-off-by line.
+include::signoff-option.txt[]
 
 --stat::
 -n::
diff --git a/Documentation/signoff-option.txt b/Documentation/signoff-option.txt
new file mode 100644 (file)
index 0000000..12aa233
--- /dev/null
@@ -0,0 +1,18 @@
+ifdef::git-commit[]
+-s::
+endif::git-commit[]
+--signoff::
+--no-signoff::
+       Add a `Signed-off-by` trailer by the committer at the end of the commit
+       log message.  The meaning of a signoff depends on the project
+       to which you're committing.  For example, it may certify that
+       the committer has the rights to submit the work under the
+       project's license or agrees to some contributor representation,
+       such as a Developer Certificate of Origin.
+       (See http://developercertificate.org for the one used by the
+       Linux kernel and Git projects.)  Consult the documentation or
+       leadership of the project to which you're contributing to
+       understand how the signoffs are used in that project.
++
+The --no-signoff option can be used to countermand an earlier --signoff
+option on the command line.
index 844629c8c441e786734c42e749293f2a718ed288..49b83ef3cc4be5dfed9e80f3edc11fd10c64fda6 100644 (file)
@@ -18,7 +18,8 @@ It is perhaps easiest to start with an example:
 More interesting possibilities exist, though, such as:
 
   * one side of history renames x -> z, and the other renames some file to
-    x/e, causing the need for the merge to do a transitive rename.
+    x/e, causing the need for the merge to do a transitive rename so that
+    the rename ends up at z/e.
 
   * one side of history renames x -> z, but also renames all files within x.
     For example, x/a -> z/alpha, x/b -> z/bravo, etc.
@@ -35,7 +36,7 @@ More interesting possibilities exist, though, such as:
     directory itself contained inner directories that were renamed to yet
     other locations).
 
-  * combinations of the above; see t/t6043-merge-rename-directories.sh for
+  * combinations of the above; see t/t6423-merge-rename-directories.sh for
     various interesting cases.
 
 Limitations -- applicability of directory renames
@@ -62,19 +63,19 @@ directory rename detection applies:
 Limitations -- detailed rules and testcases
 -------------------------------------------
 
-t/t6043-merge-rename-directories.sh contains extensive tests and commentary
+t/t6423-merge-rename-directories.sh contains extensive tests and commentary
 which generate and explore the rules listed above.  It also lists a few
 additional rules:
 
   a) If renames split a directory into two or more others, the directory
      with the most renames, "wins".
 
-  b) Avoid directory-rename-detection for a path, if that path is the
-     source of a rename on either side of a merge.
-
-  c) Only apply implicit directory renames to directories if the other side
+  b) Only apply implicit directory renames to directories if the other side
      of history is the one doing the renaming.
 
+  c) Do not perform directory rename detection for directories which had no
+     new paths added to them.
+
 Limitations -- support in different commands
 --------------------------------------------
 
index 1fb0ec17059a069d092c255a04d304dc6923b68c..790a883932c4e299758c13123a9c3a5928deeba5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2767,6 +2767,9 @@ ifdef GIT_INTEROP_MAKE_OPTS
 endif
 ifdef GIT_TEST_INDEX_VERSION
        @echo GIT_TEST_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_INDEX_VERSION)))'\' >>$@+
+endif
+ifdef GIT_TEST_PERL_FATAL_WARNINGS
+       @echo GIT_TEST_PERL_FATAL_WARNINGS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_PERL_FATAL_WARNINGS)))'\' >>$@+
 endif
        @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
 
@@ -3050,9 +3053,6 @@ quick-install-html:
 
 ### Maintainer's dist rules
 
-# Allow tweaking to hide local environment effects, like perm bits.
-# With GNU tar, "--mode=u+rwX,og+rX,og-w" would be a good idea, for example.
-TAR_DIST_EXTRA_OPTS =
 GIT_TARNAME = git-$(GIT_VERSION)
 GIT_ARCHIVE_EXTRA_FILES = \
        --prefix=$(GIT_TARNAME)/ \
@@ -3102,11 +3102,15 @@ artifacts-tar:: $(ALL_COMMANDS_TO_INSTALL) $(SCRIPT_LIB) $(OTHER_PROGRAMS) \
 htmldocs = git-htmldocs-$(GIT_VERSION)
 manpages = git-manpages-$(GIT_VERSION)
 .PHONY: dist-doc distclean
-dist-doc:
+dist-doc: git$X
        $(RM) -r .doc-tmp-dir
        mkdir .doc-tmp-dir
        $(MAKE) -C Documentation WEBDOC_DEST=../.doc-tmp-dir install-webdoc
-       cd .doc-tmp-dir && $(TAR) cf ../$(htmldocs).tar $(TAR_DIST_EXTRA_OPTS) .
+       ./git -C .doc-tmp-dir init
+       ./git -C .doc-tmp-dir add .
+       ./git -C .doc-tmp-dir commit -m htmldocs
+       ./git -C .doc-tmp-dir archive --format=tar --prefix=./ HEAD^{tree} \
+               > $(htmldocs).tar
        gzip -n -9 -f $(htmldocs).tar
        :
        $(RM) -r .doc-tmp-dir
@@ -3116,7 +3120,11 @@ dist-doc:
                man5dir=../.doc-tmp-dir/man5 \
                man7dir=../.doc-tmp-dir/man7 \
                install
-       cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar $(TAR_DIST_EXTRA_OPTS) .
+       ./git -C .doc-tmp-dir init
+       ./git -C .doc-tmp-dir add .
+       ./git -C .doc-tmp-dir commit -m manpages
+       ./git -C .doc-tmp-dir archive --format=tar --prefix=./ HEAD^{tree} \
+               > $(manpages).tar
        gzip -n -9 -f $(manpages).tar
        $(RM) -r .doc-tmp-dir
 
index bd94bd3a7c9e86f4af6d9b887c824ff5be2db9a5..be4cf6e9e5d733e477737be5d5a77fe3ccf48e24 100644 (file)
@@ -1695,6 +1695,14 @@ int run_add_p(struct repository *r, enum add_p_mode mode,
        if (mode == ADD_P_STASH)
                s.mode = &patch_mode_stash;
        else if (mode == ADD_P_RESET) {
+               /*
+                * NEEDSWORK: Instead of comparing to the literal "HEAD",
+                * compare the commit objects instead so that other ways of
+                * saying the same thing (such as "@") are also handled
+                * appropriately.
+                *
+                * This applies to the cases below too.
+                */
                if (!revision || !strcmp(revision, "HEAD"))
                        s.mode = &patch_mode_reset_head;
                else
index f0a3d32d20687d1caadc1534aa15cf50543c27c0..164742305fd147c9f31fab6b127a10e1f8e0f636 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -11,6 +11,7 @@ int advice_push_already_exists = 1;
 int advice_push_fetch_first = 1;
 int advice_push_needs_force = 1;
 int advice_push_unqualified_ref_name = 1;
+int advice_push_ref_needs_update = 1;
 int advice_status_hints = 1;
 int advice_status_u_option = 1;
 int advice_status_ahead_behind_warning = 1;
@@ -72,6 +73,7 @@ static struct {
        { "pushFetchFirst", &advice_push_fetch_first },
        { "pushNeedsForce", &advice_push_needs_force },
        { "pushUnqualifiedRefName", &advice_push_unqualified_ref_name },
+       { "pushRefNeedsUpdate", &advice_push_ref_needs_update },
        { "statusHints", &advice_status_hints },
        { "statusUoption", &advice_status_u_option },
        { "statusAheadBehindWarning", &advice_status_ahead_behind_warning },
@@ -116,6 +118,7 @@ static struct {
        [ADVICE_PUSH_ALREADY_EXISTS]                    = { "pushAlreadyExists", 1 },
        [ADVICE_PUSH_FETCH_FIRST]                       = { "pushFetchFirst", 1 },
        [ADVICE_PUSH_NEEDS_FORCE]                       = { "pushNeedsForce", 1 },
+       [ADVICE_PUSH_REF_NEEDS_UPDATE]                  = { "pushRefNeedsUpdate", 1 },
 
        /* make this an alias for backward compatibility */
        [ADVICE_PUSH_UPDATE_REJECTED_ALIAS]             = { "pushNonFastForward", 1 },
index 16f2c11642a7e63c2b61e8cdba221f024d1069e5..bc2432980a83d570ea1179ebb5d25c93e94dc238 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -11,6 +11,7 @@ extern int advice_push_already_exists;
 extern int advice_push_fetch_first;
 extern int advice_push_needs_force;
 extern int advice_push_unqualified_ref_name;
+extern int advice_push_ref_needs_update;
 extern int advice_status_hints;
 extern int advice_status_u_option;
 extern int advice_status_ahead_behind_warning;
@@ -60,6 +61,7 @@ extern int advice_add_empty_pathspec;
        ADVICE_PUSH_UNQUALIFIED_REF_NAME,
        ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
        ADVICE_PUSH_UPDATE_REJECTED,
+       ADVICE_PUSH_REF_NEEDS_UPDATE,
        ADVICE_RESET_QUIET_WARNING,
        ADVICE_RESOLVE_CONFLICT,
        ADVICE_RM_HINTS,
diff --git a/apply.c b/apply.c
index 76dba93c974b3814117e3856d875e7e4381d2f62..359ceb632ccc68c76791b98aab277fb0ed62442a 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -4699,8 +4699,13 @@ static int apply_patch(struct apply_state *state,
                        reverse_patches(patch);
                if (use_patch(state, patch)) {
                        patch_stats(state, patch);
-                       *listp = patch;
-                       listp = &patch->next;
+                       if (!list || !state->apply_in_reverse) {
+                               *listp = patch;
+                               listp = &patch->next;
+                       } else {
+                               patch->next = list;
+                               list = patch;
+                       }
 
                        if ((patch->new_name &&
                             ends_with_path_components(patch->new_name,
diff --git a/blame.c b/blame.c
index 686845b2b43dffe35d9f9811b61c80a0e0536332..de7b5d411fe3386661bea93388589977c67b3929 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -2670,7 +2670,7 @@ static struct commit *find_single_final(struct rev_info *revs,
                if (obj->flags & UNINTERESTING)
                        continue;
                obj = deref_tag(revs->repo, obj, NULL, 0);
-               if (obj->type != OBJ_COMMIT)
+               if (!obj || obj->type != OBJ_COMMIT)
                        die("Non commit %s?", revs->pending.objects[i].name);
                if (found)
                        die("More than one commit to dig from %s and %s?",
@@ -2701,7 +2701,7 @@ static struct commit *dwim_reverse_initial(struct rev_info *revs,
        /* Is that sole rev a committish? */
        obj = revs->pending.objects[0].item;
        obj = deref_tag(revs->repo, obj, NULL, 0);
-       if (obj->type != OBJ_COMMIT)
+       if (!obj || obj->type != OBJ_COMMIT)
                return NULL;
 
        /* Do we have HEAD? */
@@ -2737,7 +2737,7 @@ static struct commit *find_single_initial(struct rev_info *revs,
                if (!(obj->flags & UNINTERESTING))
                        continue;
                obj = deref_tag(revs->repo, obj, NULL, 0);
-               if (obj->type != OBJ_COMMIT)
+               if (!obj || obj->type != OBJ_COMMIT)
                        die("Non commit %s?", revs->pending.objects[i].name);
                if (found)
                        die("More than one commit to dig up from, %s and %s?",
index 4949535a7f1f926cbe01be09b5679660126074aa..f22c73a05b0472dcf5ee1002282bd9f0c067b53b 100644 (file)
@@ -98,8 +98,6 @@ struct am_state {
        char *author_name;
        char *author_email;
        char *author_date;
-       char *committer_name;
-       char *committer_email;
        char *msg;
        size_t msg_len;
 
@@ -132,8 +130,6 @@ struct am_state {
  */
 static void am_state_init(struct am_state *state)
 {
-       const char *committer;
-       struct ident_split id;
        int gpgsign;
 
        memset(state, 0, sizeof(*state));
@@ -154,14 +150,6 @@ static void am_state_init(struct am_state *state)
 
        if (!git_config_get_bool("commit.gpgsign", &gpgsign))
                state->sign_commit = gpgsign ? "" : NULL;
-
-       committer = git_committer_info(IDENT_STRICT);
-       if (split_ident_line(&id, committer, strlen(committer)) < 0)
-               die(_("invalid committer: %s"), committer);
-       state->committer_name =
-               xmemdupz(id.name_begin, id.name_end - id.name_begin);
-       state->committer_email =
-               xmemdupz(id.mail_begin, id.mail_end - id.mail_begin);
 }
 
 /**
@@ -173,8 +161,6 @@ static void am_state_release(struct am_state *state)
        free(state->author_name);
        free(state->author_email);
        free(state->author_date);
-       free(state->committer_name);
-       free(state->committer_email);
        free(state->msg);
        strvec_clear(&state->git_apply_opts);
 }
@@ -1594,8 +1580,9 @@ static void do_commit(const struct am_state *state)
                        IDENT_STRICT);
 
        if (state->committer_date_is_author_date)
-               committer = fmt_ident(state->committer_name,
-                                     state->committer_email, WANT_COMMITTER_IDENT,
+               committer = fmt_ident(getenv("GIT_COMMITTER_NAME"),
+                                     getenv("GIT_COMMITTER_EMAIL"),
+                                     WANT_COMMITTER_IDENT,
                                      state->ignore_date ? NULL
                                                         : state->author_date,
                                      IDENT_STRICT);
@@ -2237,7 +2224,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                        N_("allow fall back on 3way merging if needed")),
                OPT__QUIET(&state.quiet, N_("be quiet")),
                OPT_SET_INT('s', "signoff", &state.signoff,
-                       N_("add a Signed-off-by line to the commit message"),
+                       N_("add a Signed-off-by trailer to the commit message"),
                        SIGNOFF_EXPLICIT),
                OPT_BOOL('u', "utf8", &state.utf8,
                        N_("recode into utf8 (default)")),
index 7512b880f0d67fbe5dba8de93271324b8338d7d9..709eb713a3ba469f39ef5b4536583a84cc0c4a52 100644 (file)
@@ -20,9 +20,6 @@ static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
 static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
 
 static const char * const git_bisect_helper_usage[] = {
-       N_("git bisect--helper --next-all"),
-       N_("git bisect--helper --write-terms <bad_term> <good_term>"),
-       N_("git bisect--helper --bisect-clean-state"),
        N_("git bisect--helper --bisect-reset [<commit>]"),
        N_("git bisect--helper --bisect-write [--no-log] <state> <revision> <good_term> <bad_term>"),
        N_("git bisect--helper --bisect-check-and-set-terms <command> <good_term> <bad_term>"),
@@ -32,7 +29,8 @@ static const char * const git_bisect_helper_usage[] = {
                                            " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
        N_("git bisect--helper --bisect-next"),
        N_("git bisect--helper --bisect-auto-next"),
-       N_("git bisect--helper --bisect-autostart"),
+       N_("git bisect--helper --bisect-state (bad|new) [<rev>]"),
+       N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
        NULL
 };
 
@@ -85,6 +83,19 @@ static int one_of(const char *term, ...)
        return res;
 }
 
+/*
+ * return code BISECT_INTERNAL_SUCCESS_MERGE_BASE
+ * and BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND are codes
+ * that indicate special success.
+ */
+
+static int is_bisect_success(enum bisect_error res)
+{
+       return !res ||
+               res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND ||
+               res == BISECT_INTERNAL_SUCCESS_MERGE_BASE;
+}
+
 static int write_in_file(const char *path, const char *mode, const char *format, va_list args)
 {
        FILE *fp = NULL;
@@ -174,30 +185,6 @@ static int write_terms(const char *bad, const char *good)
        return res;
 }
 
-static int is_expected_rev(const char *expected_hex)
-{
-       struct strbuf actual_hex = STRBUF_INIT;
-       int res = 0;
-       if (strbuf_read_file(&actual_hex, git_path_bisect_expected_rev(), 0) >= 40) {
-               strbuf_trim(&actual_hex);
-               res = !strcmp(actual_hex.buf, expected_hex);
-       }
-       strbuf_release(&actual_hex);
-       return res;
-}
-
-static void check_expected_revs(const char **revs, int rev_nr)
-{
-       int i;
-
-       for (i = 0; i < rev_nr; i++) {
-               if (!is_expected_rev(revs[i])) {
-                       unlink_or_warn(git_path_bisect_ancestors_ok());
-                       unlink_or_warn(git_path_bisect_expected_rev());
-               }
-       }
-}
-
 static int bisect_reset(const char *commit)
 {
        struct strbuf branch = STRBUF_INIT;
@@ -609,12 +596,13 @@ static enum bisect_error bisect_auto_next(struct bisect_terms *terms, const char
        return bisect_next(terms, prefix);
 }
 
-static int bisect_start(struct bisect_terms *terms, const char **argv, int argc)
+static enum bisect_error bisect_start(struct bisect_terms *terms, const char **argv, int argc)
 {
        int no_checkout = 0;
        int first_parent_only = 0;
        int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0;
-       int flags, pathspec_pos, res = 0;
+       int flags, pathspec_pos;
+       enum bisect_error res = BISECT_OK;
        struct string_list revs = STRING_LIST_INIT_DUP;
        struct string_list states = STRING_LIST_INIT_DUP;
        struct strbuf start_head = STRBUF_INIT;
@@ -753,14 +741,7 @@ static int bisect_start(struct bisect_terms *terms, const char **argv, int argc)
         * Get rid of any old bisect state.
         */
        if (bisect_clean_state())
-               return -1;
-
-       /*
-        * In case of mistaken revs or checkout error, or signals received,
-        * "bisect_auto_next" below may exit or misbehave.
-        * We have to trap this to be able to clean up using
-        * "bisect_clean_state".
-        */
+               return BISECT_FAILED;
 
        /*
         * Write new start state
@@ -777,7 +758,7 @@ static int bisect_start(struct bisect_terms *terms, const char **argv, int argc)
                }
                if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0,
                               UPDATE_REFS_MSG_ON_ERR)) {
-                       res = -1;
+                       res = BISECT_FAILED;
                        goto finish;
                }
        }
@@ -789,25 +770,31 @@ static int bisect_start(struct bisect_terms *terms, const char **argv, int argc)
        for (i = 0; i < states.nr; i++)
                if (bisect_write(states.items[i].string,
                                 revs.items[i].string, terms, 1)) {
-                       res = -1;
+                       res = BISECT_FAILED;
                        goto finish;
                }
 
        if (must_write_terms && write_terms(terms->term_bad,
                                            terms->term_good)) {
-               res = -1;
+               res = BISECT_FAILED;
                goto finish;
        }
 
        res = bisect_append_log_quoted(argv);
        if (res)
-               res = -1;
+               res = BISECT_FAILED;
 
 finish:
        string_list_clear(&revs, 0);
        string_list_clear(&states, 0);
        strbuf_release(&start_head);
        strbuf_release(&bisect_names);
+       if (res)
+               return res;
+
+       res = bisect_auto_next(terms, NULL);
+       if (!is_bisect_success(res))
+               bisect_clean_state();
        return res;
 }
 
@@ -843,14 +830,84 @@ static int bisect_autostart(struct bisect_terms *terms)
        return res;
 }
 
+static enum bisect_error bisect_state(struct bisect_terms *terms, const char **argv,
+                                     int argc)
+{
+       const char *state;
+       int i, verify_expected = 1;
+       struct object_id oid, expected;
+       struct strbuf buf = STRBUF_INIT;
+       struct oid_array revs = OID_ARRAY_INIT;
+
+       if (!argc)
+               return error(_("Please call `--bisect-state` with at least one argument"));
+
+       if (bisect_autostart(terms))
+               return BISECT_FAILED;
+
+       state = argv[0];
+       if (check_and_set_terms(terms, state) ||
+           !one_of(state, terms->term_good, terms->term_bad, "skip", NULL))
+               return BISECT_FAILED;
+
+       argv++;
+       argc--;
+       if (argc > 1 && !strcmp(state, terms->term_bad))
+               return error(_("'git bisect %s' can take only one argument."), terms->term_bad);
+
+       if (argc == 0) {
+               const char *head = "BISECT_HEAD";
+               enum get_oid_result res_head = get_oid(head, &oid);
+
+               if (res_head == MISSING_OBJECT) {
+                       head = "HEAD";
+                       res_head = get_oid(head, &oid);
+               }
+
+               if (res_head)
+                       error(_("Bad rev input: %s"), head);
+               oid_array_append(&revs, &oid);
+       }
+
+       /*
+        * All input revs must be checked before executing bisect_write()
+        * to discard junk revs.
+        */
+
+       for (; argc; argc--, argv++) {
+               if (get_oid(*argv, &oid)){
+                       error(_("Bad rev input: %s"), *argv);
+                       oid_array_clear(&revs);
+                       return BISECT_FAILED;
+               }
+               oid_array_append(&revs, &oid);
+       }
+
+       if (strbuf_read_file(&buf, git_path_bisect_expected_rev(), 0) < the_hash_algo->hexsz ||
+           get_oid_hex(buf.buf, &expected) < 0)
+               verify_expected = 0; /* Ignore invalid file contents */
+       strbuf_release(&buf);
+
+       for (i = 0; i < revs.nr; i++) {
+               if (bisect_write(state, oid_to_hex(&revs.oid[i]), terms, 0)) {
+                       oid_array_clear(&revs);
+                       return BISECT_FAILED;
+               }
+               if (verify_expected && !oideq(&revs.oid[i], &expected)) {
+                       unlink_or_warn(git_path_bisect_ancestors_ok());
+                       unlink_or_warn(git_path_bisect_expected_rev());
+                       verify_expected = 0;
+               }
+       }
+
+       oid_array_clear(&revs);
+       return bisect_auto_next(terms, NULL);
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
        enum {
-               NEXT_ALL = 1,
-               WRITE_TERMS,
-               BISECT_CLEAN_STATE,
-               CHECK_EXPECTED_REVS,
-               BISECT_RESET,
+               BISECT_RESET = 1,
                BISECT_WRITE,
                CHECK_AND_SET_TERMS,
                BISECT_NEXT_CHECK,
@@ -858,18 +915,11 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
                BISECT_START,
                BISECT_AUTOSTART,
                BISECT_NEXT,
-               BISECT_AUTO_NEXT
+               BISECT_AUTO_NEXT,
+               BISECT_STATE
        } cmdmode = 0;
        int res = 0, nolog = 0;
        struct option options[] = {
-               OPT_CMDMODE(0, "next-all", &cmdmode,
-                        N_("perform 'git bisect next'"), NEXT_ALL),
-               OPT_CMDMODE(0, "write-terms", &cmdmode,
-                        N_("write the terms to .git/BISECT_TERMS"), WRITE_TERMS),
-               OPT_CMDMODE(0, "bisect-clean-state", &cmdmode,
-                        N_("cleanup the bisection state"), BISECT_CLEAN_STATE),
-               OPT_CMDMODE(0, "check-expected-revs", &cmdmode,
-                        N_("check for expected revs"), CHECK_EXPECTED_REVS),
                OPT_CMDMODE(0, "bisect-reset", &cmdmode,
                         N_("reset the bisection state"), BISECT_RESET),
                OPT_CMDMODE(0, "bisect-write", &cmdmode,
@@ -886,8 +936,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
                         N_("find the next bisection commit"), BISECT_NEXT),
                OPT_CMDMODE(0, "bisect-auto-next", &cmdmode,
                         N_("verify the next bisection state then checkout the next bisection commit"), BISECT_AUTO_NEXT),
-               OPT_CMDMODE(0, "bisect-autostart", &cmdmode,
-                        N_("start the bisection if it has not yet been started"), BISECT_AUTOSTART),
+               OPT_CMDMODE(0, "bisect-state", &cmdmode,
+                        N_("mark the state of ref (or refs)"), BISECT_STATE),
                OPT_BOOL(0, "no-log", &nolog,
                         N_("no log for BISECT_WRITE")),
                OPT_END()
@@ -902,20 +952,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
                usage_with_options(git_bisect_helper_usage, options);
 
        switch (cmdmode) {
-       case NEXT_ALL:
-               res = bisect_next_all(the_repository, prefix);
-               break;
-       case WRITE_TERMS:
-               if (argc != 2)
-                       return error(_("--write-terms requires two arguments"));
-               return write_terms(argv[0], argv[1]);
-       case BISECT_CLEAN_STATE:
-               if (argc != 0)
-                       return error(_("--bisect-clean-state requires no arguments"));
-               return bisect_clean_state();
-       case CHECK_EXPECTED_REVS:
-               check_expected_revs(argv, argc);
-               return 0;
        case BISECT_RESET:
                if (argc > 1)
                        return error(_("--bisect-reset requires either no argument or a commit"));
@@ -959,11 +995,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
                get_terms(&terms);
                res = bisect_auto_next(&terms, prefix);
                break;
-       case BISECT_AUTOSTART:
-               if (argc)
-                       return error(_("--bisect-autostart does not accept arguments"));
+       case BISECT_STATE:
                set_terms(&terms, "bad", "good");
-               res = bisect_autostart(&terms);
+               get_terms(&terms);
+               res = bisect_state(&terms, argv, argc);
                break;
        default:
                BUG("unknown subcommand %d", cmdmode);
index bb0f29300e5ca4caf3a6b9fa8e4ae96ce38c34d1..b5036ab3277ef10ab94d4b50df32f1251f788fe0 100644 (file)
@@ -820,6 +820,8 @@ static int peel_to_commit_oid(struct object_id *oid_ret, void *cbdata)
                if (kind != OBJ_TAG)
                        return -1;
                obj = deref_tag(r, parse_object(r, &oid), NULL, 0);
+               if (!obj)
+                       return -1;
                oidcpy(&oid, &obj->oid);
        }
 }
index a854fd16e779123f7d39f685fa3b7eb7c1ba930e..4bbfc92dce5a0e71e4389c16fd096f4a22fef4a1 100644 (file)
@@ -79,6 +79,14 @@ static int checkout_file(const char *name, const char *prefix)
                return errs > 0 ? -1 : 0;
        }
 
+       /*
+        * At this point we know we didn't try to check anything out. If it was
+        * because we did find an entry but it was stage 0, that's not an
+        * error.
+        */
+       if (has_same_name && checkout_stage == CHECKOUT_ALL)
+               return 0;
+
        if (!state.quiet) {
                fprintf(stderr, "git checkout-index: %s ", name);
                if (!has_same_name)
@@ -159,6 +167,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
        int prefix_length;
        int force = 0, quiet = 0, not_new = 0;
        int index_opt = 0;
+       int err = 0;
        struct option builtin_checkout_index_options[] = {
                OPT_BOOL('a', "all", &all,
                        N_("check out all files in the index")),
@@ -223,7 +232,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
                if (read_from_stdin)
                        die("git checkout-index: don't mix '--stdin' and explicit filenames");
                p = prefix_path(prefix, prefix_length, arg);
-               checkout_file(p, prefix);
+               err |= checkout_file(p, prefix);
                free(p);
        }
 
@@ -245,13 +254,16 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
                                strbuf_swap(&buf, &unquoted);
                        }
                        p = prefix_path(prefix, prefix_length, buf.buf);
-                       checkout_file(p, prefix);
+                       err |= checkout_file(p, prefix);
                        free(p);
                }
                strbuf_release(&unquoted);
                strbuf_release(&buf);
        }
 
+       if (err)
+               return 1;
+
        if (all)
                checkout_all(prefix, prefix_length);
 
index 0951f8fee5cc2a88ac9e08009a69eaf446b6fed4..7c311cecb3be6f9cb9c66632ef9eb80e1ab1ea45 100644 (file)
@@ -471,6 +471,19 @@ static int checkout_paths(const struct checkout_opts *opts,
 
        if (opts->patch_mode) {
                const char *patch_mode;
+               const char *rev = new_branch_info->name;
+               char rev_oid[GIT_MAX_HEXSZ + 1];
+
+               /*
+                * Since rev can be in the form of `<a>...<b>` (which is not
+                * recognized by diff-index), we will always replace the name
+                * with the hex of the commit (whether it's in `...` form or
+                * not) for the run_add_interactive() machinery to work
+                * properly. However, there is special logic for the HEAD case
+                * so we mustn't replace that.
+                */
+               if (rev && strcmp(rev, "HEAD"))
+                       rev = oid_to_hex_r(rev_oid, &new_branch_info->commit->object.oid);
 
                if (opts->checkout_index && opts->checkout_worktree)
                        patch_mode = "--patch=checkout";
@@ -481,7 +494,7 @@ static int checkout_paths(const struct checkout_opts *opts,
                else
                        BUG("either flag must have been set, worktree=%d, index=%d",
                            opts->checkout_worktree, opts->checkout_index);
-               return run_add_interactive(new_branch_info->name, patch_mode, &opts->pathspec);
+               return run_add_interactive(rev, patch_mode, &opts->pathspec);
        }
 
        repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
@@ -1093,11 +1106,16 @@ static int switch_branches(const struct checkout_opts *opts,
 
 static int git_checkout_config(const char *var, const char *value, void *cb)
 {
+       struct checkout_opts *opts = cb;
+
        if (!strcmp(var, "diff.ignoresubmodules")) {
-               struct checkout_opts *opts = cb;
                handle_ignore_submodules_arg(&opts->diff_options, value);
                return 0;
        }
+       if (!strcmp(var, "checkout.guess")) {
+               opts->dwim_new_local_branch = git_config_bool(var, value);
+               return 0;
+       }
 
        if (starts_with(var, "submodule."))
                return git_default_submodule_config(var, value, NULL);
index 391aa41075ee75fe20a7b9a4d79e73cd90e10375..a0841923cfe03663068d47ea2cc78a35b3dd3733 100644 (file)
@@ -53,6 +53,7 @@ static int option_shallow_submodules;
 static int deepen;
 static char *option_template, *option_depth, *option_since;
 static char *option_origin = NULL;
+static char *remote_name = NULL;
 static char *option_branch = NULL;
 static struct string_list option_not = STRING_LIST_INIT_NODUP;
 static const char *real_git_dir;
@@ -721,7 +722,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
                if (!option_bare) {
                        update_ref(msg, "HEAD", &our->old_oid, NULL, 0,
                                   UPDATE_REFS_DIE_ON_ERR);
-                       install_branch_config(0, head, option_origin, our->name);
+                       install_branch_config(0, head, remote_name, our->name);
                }
        } else if (our) {
                struct commit *c = lookup_commit_reference(the_repository,
@@ -851,8 +852,26 @@ static int checkout(int submodule_progress)
        return err;
 }
 
+static int git_clone_config(const char *k, const char *v, void *cb)
+{
+       if (!strcmp(k, "clone.defaultremotename")) {
+               free(remote_name);
+               remote_name = xstrdup(v);
+       }
+       return git_default_config(k, v, cb);
+}
+
 static int write_one_config(const char *key, const char *value, void *data)
 {
+       /*
+        * give git_clone_config a chance to write config values back to the
+        * environment, since git_config_set_multivar_gently only deals with
+        * config-file writes
+        */
+       int apply_failed = git_clone_config(key, value, data);
+       if (apply_failed)
+               return apply_failed;
+
        return git_config_set_multivar_gently(key,
                                              value ? value : "true",
                                              CONFIG_REGEX_NONE, 0);
@@ -905,12 +924,12 @@ static void write_refspec_config(const char *src_ref_prefix,
                }
                /* Configure the remote */
                if (value.len) {
-                       strbuf_addf(&key, "remote.%s.fetch", option_origin);
+                       strbuf_addf(&key, "remote.%s.fetch", remote_name);
                        git_config_set_multivar(key.buf, value.buf, "^$", 0);
                        strbuf_reset(&key);
 
                        if (option_mirror) {
-                               strbuf_addf(&key, "remote.%s.mirror", option_origin);
+                               strbuf_addf(&key, "remote.%s.mirror", remote_name);
                                git_config_set(key.buf, "true");
                                strbuf_reset(&key);
                        }
@@ -963,6 +982,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        struct strvec ref_prefixes = STRVEC_INIT;
 
        packet_trace_identity("clone");
+
+       git_config(git_clone_config, NULL);
+
        argc = parse_options(argc, argv, prefix, builtin_clone_options,
                             builtin_clone_usage, 0);
 
@@ -991,9 +1013,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                option_no_checkout = 1;
        }
 
-       if (!option_origin)
-               option_origin = "origin";
-
        repo_name = argv[0];
 
        path = get_repo_path(repo_name, &is_bundle);
@@ -1124,9 +1143,30 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (real_git_dir)
                git_dir = real_git_dir;
 
+       /*
+        * additional config can be injected with -c, make sure it's included
+        * after init_db, which clears the entire config environment.
+        */
        write_config(&option_config);
 
-       git_config(git_default_config, NULL);
+       /*
+        * re-read config after init_db and write_config to pick up any config
+        * injected by --template and --config, respectively.
+        */
+       git_config(git_clone_config, NULL);
+
+       /*
+        * apply the remote name provided by --origin only after this second
+        * call to git_config, to ensure it overrides all config-based values.
+        */
+       if (option_origin != NULL)
+               remote_name = xstrdup(option_origin);
+
+       if (remote_name == NULL)
+               remote_name = xstrdup("origin");
+
+       if (!valid_remote_name(remote_name))
+               die(_("'%s' is not a valid remote name"), remote_name);
 
        if (option_bare) {
                if (option_mirror)
@@ -1135,15 +1175,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
                git_config_set("core.bare", "true");
        } else {
-               strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin);
+               strbuf_addf(&branch_top, "refs/remotes/%s/", remote_name);
        }
 
-       strbuf_addf(&key, "remote.%s.url", option_origin);
+       strbuf_addf(&key, "remote.%s.url", remote_name);
        git_config_set(key.buf, repo);
        strbuf_reset(&key);
 
        if (option_no_tags) {
-               strbuf_addf(&key, "remote.%s.tagOpt", option_origin);
+               strbuf_addf(&key, "remote.%s.tagOpt", remote_name);
                git_config_set(key.buf, "--no-tags");
                strbuf_reset(&key);
        }
@@ -1154,7 +1194,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (option_sparse_checkout && git_sparse_checkout_init(dir))
                return 1;
 
-       remote = remote_get(option_origin);
+       remote = remote_get(remote_name);
 
        refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix,
                        branch_top.buf);
@@ -1266,7 +1306,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
                        if (!our_head_points_at)
                                die(_("Remote branch %s not found in upstream %s"),
-                                   option_branch, option_origin);
+                                   option_branch, remote_name);
                }
                else
                        our_head_points_at = remote_head_points_at;
@@ -1274,7 +1314,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        else {
                if (option_branch)
                        die(_("Remote branch %s not found in upstream %s"),
-                                       option_branch, option_origin);
+                                       option_branch, remote_name);
 
                warning(_("You appear to have cloned an empty repository."));
                mapped_refs = NULL;
@@ -1286,7 +1326,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                        const char *branch = git_default_branch_name();
                        char *ref = xstrfmt("refs/heads/%s", branch);
 
-                       install_branch_config(0, branch, option_origin, ref);
+                       install_branch_config(0, branch, remote_name, ref);
                        free(ref);
                }
        }
@@ -1295,7 +1335,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                        remote_head_points_at, &branch_top);
 
        if (filter_options.choice)
-               partial_clone_register(option_origin, &filter_options);
+               partial_clone_register(remote_name, &filter_options);
 
        if (is_local)
                clone_local(path, git_dir);
@@ -1327,6 +1367,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        junk_mode = JUNK_LEAVE_REPO;
        err = checkout(submodule_progress);
 
+       free(remote_name);
        strbuf_release(&reflog_msg);
        strbuf_release(&branch_top);
        strbuf_release(&key);
index 1dfd799ec5185928f327d2cdd3156b41d6c0199e..505fe60956db385d94aa7fd14e016a3dfeb9345c 100644 (file)
@@ -1507,7 +1507,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup specified commit")),
                OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
                OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
-               OPT_BOOL('s', "signoff", &signoff, N_("add Signed-off-by:")),
+               OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")),
                OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
                OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
                OPT_CLEANUP(&cleanup_arg),
index 879acfbcda75141d3022e9b285e191977a06ba3d..d75dcdc64aa3c3da303b117ba505ba683c1afcde 100644 (file)
@@ -1,6 +1,7 @@
 #include "git-compat-util.h"
 #include "credential.h"
 #include "builtin.h"
+#include "config.h"
 
 static const char usage_msg[] =
        "git credential [fill|approve|reject]";
@@ -10,6 +11,8 @@ int cmd_credential(int argc, const char **argv, const char *prefix)
        const char *op;
        struct credential c = CREDENTIAL_INIT;
 
+       git_config(git_default_config, NULL);
+
        if (argc != 2 || !strcmp(argv[1], "-h"))
                usage(usage_msg);
        op = argv[1];
index 93ec6424234c65f486dcfca724c77a48e5e91f91..7f5281c46168ed72505fe9ee3cfa200c200b4cb1 100644 (file)
@@ -15,7 +15,7 @@ COMMON_DIFF_OPTIONS_HELP;
 int cmd_diff_index(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
-       int cached = 0;
+       unsigned int option = 0;
        int i;
        int result;
 
@@ -32,7 +32,9 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
                const char *arg = argv[i];
 
                if (!strcmp(arg, "--cached"))
-                       cached = 1;
+                       option |= DIFF_INDEX_CACHED;
+               else if (!strcmp(arg, "--merge-base"))
+                       option |= DIFF_INDEX_MERGE_BASE;
                else
                        usage(diff_cache_usage);
        }
@@ -46,7 +48,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
        if (rev.pending.nr != 1 ||
            rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
                usage(diff_cache_usage);
-       if (!cached) {
+       if (!(option & DIFF_INDEX_CACHED)) {
                setup_work_tree();
                if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
                        perror("read_cache_preload");
@@ -56,7 +58,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
                perror("read_cache");
                return -1;
        }
-       result = run_diff_index(&rev, cached);
+       result = run_diff_index(&rev, option);
        UNLEAK(rev);
        return diff_result_code(&rev.diffopt, result);
 }
index 802363d0a2295f855828121a5b99184d6475b74f..9fc95e959f0e0dba69cc1f96597bb77f30997d1e 100644 (file)
@@ -111,6 +111,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        struct setup_revision_opt s_r_opt;
        struct userformat_want w;
        int read_stdin = 0;
+       int merge_base = 0;
 
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(diff_tree_usage);
@@ -143,9 +144,18 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                        read_stdin = 1;
                        continue;
                }
+               if (!strcmp(arg, "--merge-base")) {
+                       merge_base = 1;
+                       continue;
+               }
                usage(diff_tree_usage);
        }
 
+       if (read_stdin && merge_base)
+               die(_("--stdin and --merge-base are mutually exclusive"));
+       if (merge_base && opt->pending.nr != 2)
+               die(_("--merge-base only works with two commits"));
+
        /*
         * NOTE!  We expect "a..b" to expand to "^a b" but it is
         * perfectly valid for revision range parser to yield "b ^a",
@@ -165,7 +175,12 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        case 2:
                tree1 = opt->pending.objects[0].item;
                tree2 = opt->pending.objects[1].item;
-               if (tree2->flags & UNINTERESTING) {
+               if (merge_base) {
+                       struct object_id oid;
+
+                       diff_get_merge_base(opt, &oid);
+                       tree1 = lookup_object(the_repository, &oid);
+               } else if (tree2->flags & UNINTERESTING) {
                        SWAP(tree2, tree1);
                }
                diff_tree_oid(&tree1->oid, &tree2->oid, "", &opt->diffopt);
index cd4083fed96ecda432cf166ba12cfaf58b4b609b..780c33877f85b63c8ecf6bffbe6f7e2c5e91f5aa 100644 (file)
@@ -26,7 +26,7 @@
 static const char builtin_diff_usage[] =
 "git diff [<options>] [<commit>] [--] [<path>...]\n"
 "   or: git diff [<options>] --cached [<commit>] [--] [<path>...]\n"
-"   or: git diff [<options>] <commit> [<commit>...] <commit> [--] [<path>...]\n"
+"   or: git diff [<options>] <commit> [--merge-base] [<commit>...] <commit> [--] [<path>...]\n"
 "   or: git diff [<options>] <commit>...<commit>] [--] [<path>...]\n"
 "   or: git diff [<options>] <blob> <blob>]\n"
 "   or: git diff [<options>] --no-index [--] <path> <path>]\n"
@@ -134,11 +134,13 @@ static int builtin_diff_blobs(struct rev_info *revs,
 static int builtin_diff_index(struct rev_info *revs,
                              int argc, const char **argv)
 {
-       int cached = 0;
+       unsigned int option = 0;
        while (1 < argc) {
                const char *arg = argv[1];
                if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged"))
-                       cached = 1;
+                       option |= DIFF_INDEX_CACHED;
+               else if (!strcmp(arg, "--merge-base"))
+                       option |= DIFF_INDEX_MERGE_BASE;
                else
                        usage(builtin_diff_usage);
                argv++; argc--;
@@ -151,7 +153,7 @@ static int builtin_diff_index(struct rev_info *revs,
            revs->max_count != -1 || revs->min_age != -1 ||
            revs->max_age != -1)
                usage(builtin_diff_usage);
-       if (!cached) {
+       if (!(option & DIFF_INDEX_CACHED)) {
                setup_work_tree();
                if (read_cache_preload(&revs->diffopt.pathspec) < 0) {
                        perror("read_cache_preload");
@@ -161,7 +163,7 @@ static int builtin_diff_index(struct rev_info *revs,
                perror("read_cache");
                return -1;
        }
-       return run_diff_index(revs, cached);
+       return run_diff_index(revs, option);
 }
 
 static int builtin_diff_tree(struct rev_info *revs,
@@ -170,19 +172,34 @@ static int builtin_diff_tree(struct rev_info *revs,
                             struct object_array_entry *ent1)
 {
        const struct object_id *(oid[2]);
-       int swap = 0;
+       struct object_id mb_oid;
+       int merge_base = 0;
 
-       if (argc > 1)
-               usage(builtin_diff_usage);
+       while (1 < argc) {
+               const char *arg = argv[1];
+               if (!strcmp(arg, "--merge-base"))
+                       merge_base = 1;
+               else
+                       usage(builtin_diff_usage);
+               argv++; argc--;
+       }
 
-       /*
-        * We saw two trees, ent0 and ent1.  If ent1 is uninteresting,
-        * swap them.
-        */
-       if (ent1->item->flags & UNINTERESTING)
-               swap = 1;
-       oid[swap] = &ent0->item->oid;
-       oid[1 - swap] = &ent1->item->oid;
+       if (merge_base) {
+               diff_get_merge_base(revs, &mb_oid);
+               oid[0] = &mb_oid;
+               oid[1] = &revs->pending.objects[1].item->oid;
+       } else {
+               int swap = 0;
+
+               /*
+                * We saw two trees, ent0 and ent1.  If ent1 is uninteresting,
+                * swap them.
+                */
+               if (ent1->item->flags & UNINTERESTING)
+                       swap = 1;
+               oid[swap] = &ent0->item->oid;
+               oid[1 - swap] = &ent1->item->oid;
+       }
        diff_tree_oid(oid[0], oid[1], "", &revs->diffopt);
        log_tree_diff_flush(revs);
        return 0;
index 1bf50a73dc3597394ecf73e5a786fe217e7ef633..dd4d09cecebd57a6d89c3b8224a5435f26c5a5d2 100644 (file)
@@ -150,7 +150,7 @@ struct recent_command {
        char *buf;
 };
 
-typedef void (*mark_set_inserter_t)(struct mark_set *s, struct object_id *oid, uintmax_t mark);
+typedef void (*mark_set_inserter_t)(struct mark_set **s, struct object_id *oid, uintmax_t mark);
 typedef void (*each_mark_fn_t)(uintmax_t mark, void *obj, void *cbp);
 
 /* Configured limits on output */
@@ -526,13 +526,15 @@ static unsigned int hc_str(const char *s, size_t len)
        return r;
 }
 
-static void insert_mark(struct mark_set *s, uintmax_t idnum, struct object_entry *oe)
+static void insert_mark(struct mark_set **top, uintmax_t idnum, struct object_entry *oe)
 {
+       struct mark_set *s = *top;
+
        while ((idnum >> s->shift) >= 1024) {
                s = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set));
-               s->shift = marks->shift + 10;
-               s->data.sets[0] = marks;
-               marks = s;
+               s->shift = (*top)->shift + 10;
+               s->data.sets[0] = *top;
+               *top = s;
        }
        while (s->shift) {
                uintmax_t i = idnum >> s->shift;
@@ -944,7 +946,7 @@ static int store_object(
 
        e = insert_object(&oid);
        if (mark)
-               insert_mark(marks, mark, e);
+               insert_mark(&marks, mark, e);
        if (e->idx.offset) {
                duplicate_count_by_type[type]++;
                return 1;
@@ -1142,7 +1144,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
        e = insert_object(&oid);
 
        if (mark)
-               insert_mark(marks, mark, e);
+               insert_mark(&marks, mark, e);
 
        if (e->idx.offset) {
                duplicate_count_by_type[OBJ_BLOB]++;
@@ -1717,7 +1719,7 @@ static void dump_marks(void)
        }
 }
 
-static void insert_object_entry(struct mark_set *s, struct object_id *oid, uintmax_t mark)
+static void insert_object_entry(struct mark_set **s, struct object_id *oid, uintmax_t mark)
 {
        struct object_entry *e;
        e = find_object(oid);
@@ -1734,12 +1736,12 @@ static void insert_object_entry(struct mark_set *s, struct object_id *oid, uintm
        insert_mark(s, mark, e);
 }
 
-static void insert_oid_entry(struct mark_set *s, struct object_id *oid, uintmax_t mark)
+static void insert_oid_entry(struct mark_set **s, struct object_id *oid, uintmax_t mark)
 {
        insert_mark(s, mark, xmemdupz(oid, sizeof(*oid)));
 }
 
-static void read_mark_file(struct mark_set *s, FILE *f, mark_set_inserter_t inserter)
+static void read_mark_file(struct mark_set **s, FILE *f, mark_set_inserter_t inserter)
 {
        char line[512];
        while (fgets(line, sizeof(line), f)) {
@@ -1772,7 +1774,7 @@ static void read_marks(void)
                goto done; /* Marks file does not exist */
        else
                die_errno("cannot read '%s'", import_marks_file);
-       read_mark_file(marks, f, insert_object_entry);
+       read_mark_file(&marks, f, insert_object_entry);
        fclose(f);
 done:
        import_marks_file_done = 1;
@@ -3228,7 +3230,7 @@ static void parse_alias(void)
                die(_("Expected 'to' command, got %s"), command_buf.buf);
        e = find_object(&b.oid);
        assert(e);
-       insert_mark(marks, next_mark, e);
+       insert_mark(&marks, next_mark, e);
 }
 
 static char* make_fast_import_path(const char *path)
@@ -3321,13 +3323,14 @@ static void option_rewrite_submodules(const char *arg, struct string_list *list)
        *f = '\0';
        f++;
        ms = xcalloc(1, sizeof(*ms));
-       string_list_insert(list, s)->util = ms;
 
        fp = fopen(f, "r");
        if (!fp)
                die_errno("cannot read '%s'", f);
-       read_mark_file(ms, fp, insert_oid_entry);
+       read_mark_file(&ms, fp, insert_oid_entry);
        fclose(fp);
+
+       string_list_insert(list, s)->util = ms;
 }
 
 static int parse_one_option(const char *option)
@@ -3396,7 +3399,6 @@ static int parse_one_feature(const char *feature, int from_stream)
                option_rewrite_submodules(arg, &sub_marks_to);
        } else if (skip_prefix(feature, "rewrite-submodules-from=", &arg)) {
                option_rewrite_submodules(arg, &sub_marks_from);
-       } else if (skip_prefix(feature, "rewrite-submodules-from=", &arg)) {
        } else if (!strcmp(feature, "get-mark")) {
                ; /* Don't die - this feature is supported */
        } else if (!strcmp(feature, "cat-blob")) {
index 090959350e06c0821889cab4d3a217ef814e5618..3629a82299fb77adb9708de18a447a4dbf3ed3a6 100644 (file)
@@ -29,6 +29,8 @@
 #include "tree.h"
 #include "promisor-remote.h"
 #include "refs.h"
+#include "remote.h"
+#include "object-store.h"
 
 #define FAILED_RUN "failed to run %s"
 
@@ -737,9 +739,15 @@ static int dfs_on_ref(const char *refname,
        commit = lookup_commit(the_repository, oid);
        if (!commit)
                return 0;
-       if (parse_commit(commit))
+       if (parse_commit(commit) ||
+           commit_graph_position(commit) != COMMIT_NOT_FROM_GRAPH)
                return 0;
 
+       data->num_not_in_graph++;
+
+       if (data->num_not_in_graph >= data->limit)
+               return 1;
+
        commit_list_append(commit, &stack);
 
        while (!result && stack) {
@@ -807,6 +815,10 @@ static int run_write_commit_graph(struct maintenance_run_opts *opts)
 
 static int maintenance_task_commit_graph(struct maintenance_run_opts *opts)
 {
+       prepare_repo_settings(the_repository);
+       if (!the_repository->settings.core_commit_graph)
+               return 0;
+
        close_object_store(the_repository->objects);
        if (run_write_commit_graph(opts)) {
                error(_("failed to write commit-graph"));
@@ -816,6 +828,51 @@ static int maintenance_task_commit_graph(struct maintenance_run_opts *opts)
        return 0;
 }
 
+static int fetch_remote(const char *remote, struct maintenance_run_opts *opts)
+{
+       struct child_process child = CHILD_PROCESS_INIT;
+
+       child.git_cmd = 1;
+       strvec_pushl(&child.args, "fetch", remote, "--prune", "--no-tags",
+                    "--no-write-fetch-head", "--recurse-submodules=no",
+                    "--refmap=", NULL);
+
+       if (opts->quiet)
+               strvec_push(&child.args, "--quiet");
+
+       strvec_pushf(&child.args, "+refs/heads/*:refs/prefetch/%s/*", remote);
+
+       return !!run_command(&child);
+}
+
+static int append_remote(struct remote *remote, void *cbdata)
+{
+       struct string_list *remotes = (struct string_list *)cbdata;
+
+       string_list_append(remotes, remote->name);
+       return 0;
+}
+
+static int maintenance_task_prefetch(struct maintenance_run_opts *opts)
+{
+       int result = 0;
+       struct string_list_item *item;
+       struct string_list remotes = STRING_LIST_INIT_DUP;
+
+       if (for_each_remote(append_remote, &remotes)) {
+               error(_("failed to fill remotes"));
+               result = 1;
+               goto cleanup;
+       }
+
+       for_each_string_list_item(item, &remotes)
+               result |= fetch_remote(item->string, opts);
+
+cleanup:
+       string_list_clear(&remotes, 0);
+       return result;
+}
+
 static int maintenance_task_gc(struct maintenance_run_opts *opts)
 {
        struct child_process child = CHILD_PROCESS_INIT;
@@ -834,6 +891,268 @@ static int maintenance_task_gc(struct maintenance_run_opts *opts)
        return run_command(&child);
 }
 
+static int prune_packed(struct maintenance_run_opts *opts)
+{
+       struct child_process child = CHILD_PROCESS_INIT;
+
+       child.git_cmd = 1;
+       strvec_push(&child.args, "prune-packed");
+
+       if (opts->quiet)
+               strvec_push(&child.args, "--quiet");
+
+       return !!run_command(&child);
+}
+
+struct write_loose_object_data {
+       FILE *in;
+       int count;
+       int batch_size;
+};
+
+static int loose_object_auto_limit = 100;
+
+static int loose_object_count(const struct object_id *oid,
+                              const char *path,
+                              void *data)
+{
+       int *count = (int*)data;
+       if (++(*count) >= loose_object_auto_limit)
+               return 1;
+       return 0;
+}
+
+static int loose_object_auto_condition(void)
+{
+       int count = 0;
+
+       git_config_get_int("maintenance.loose-objects.auto",
+                          &loose_object_auto_limit);
+
+       if (!loose_object_auto_limit)
+               return 0;
+       if (loose_object_auto_limit < 0)
+               return 1;
+
+       return for_each_loose_file_in_objdir(the_repository->objects->odb->path,
+                                            loose_object_count,
+                                            NULL, NULL, &count);
+}
+
+static int bail_on_loose(const struct object_id *oid,
+                        const char *path,
+                        void *data)
+{
+       return 1;
+}
+
+static int write_loose_object_to_stdin(const struct object_id *oid,
+                                      const char *path,
+                                      void *data)
+{
+       struct write_loose_object_data *d = (struct write_loose_object_data *)data;
+
+       fprintf(d->in, "%s\n", oid_to_hex(oid));
+
+       return ++(d->count) > d->batch_size;
+}
+
+static int pack_loose(struct maintenance_run_opts *opts)
+{
+       struct repository *r = the_repository;
+       int result = 0;
+       struct write_loose_object_data data;
+       struct child_process pack_proc = CHILD_PROCESS_INIT;
+
+       /*
+        * Do not start pack-objects process
+        * if there are no loose objects.
+        */
+       if (!for_each_loose_file_in_objdir(r->objects->odb->path,
+                                          bail_on_loose,
+                                          NULL, NULL, NULL))
+               return 0;
+
+       pack_proc.git_cmd = 1;
+
+       strvec_push(&pack_proc.args, "pack-objects");
+       if (opts->quiet)
+               strvec_push(&pack_proc.args, "--quiet");
+       strvec_pushf(&pack_proc.args, "%s/pack/loose", r->objects->odb->path);
+
+       pack_proc.in = -1;
+
+       if (start_command(&pack_proc)) {
+               error(_("failed to start 'git pack-objects' process"));
+               return 1;
+       }
+
+       data.in = xfdopen(pack_proc.in, "w");
+       data.count = 0;
+       data.batch_size = 50000;
+
+       for_each_loose_file_in_objdir(r->objects->odb->path,
+                                     write_loose_object_to_stdin,
+                                     NULL,
+                                     NULL,
+                                     &data);
+
+       fclose(data.in);
+
+       if (finish_command(&pack_proc)) {
+               error(_("failed to finish 'git pack-objects' process"));
+               result = 1;
+       }
+
+       return result;
+}
+
+static int maintenance_task_loose_objects(struct maintenance_run_opts *opts)
+{
+       return prune_packed(opts) || pack_loose(opts);
+}
+
+static int incremental_repack_auto_condition(void)
+{
+       struct packed_git *p;
+       int enabled;
+       int incremental_repack_auto_limit = 10;
+       int count = 0;
+
+       if (git_config_get_bool("core.multiPackIndex", &enabled) ||
+           !enabled)
+               return 0;
+
+       git_config_get_int("maintenance.incremental-repack.auto",
+                          &incremental_repack_auto_limit);
+
+       if (!incremental_repack_auto_limit)
+               return 0;
+       if (incremental_repack_auto_limit < 0)
+               return 1;
+
+       for (p = get_packed_git(the_repository);
+            count < incremental_repack_auto_limit && p;
+            p = p->next) {
+               if (!p->multi_pack_index)
+                       count++;
+       }
+
+       return count >= incremental_repack_auto_limit;
+}
+
+static int multi_pack_index_write(struct maintenance_run_opts *opts)
+{
+       struct child_process child = CHILD_PROCESS_INIT;
+
+       child.git_cmd = 1;
+       strvec_pushl(&child.args, "multi-pack-index", "write", NULL);
+
+       if (opts->quiet)
+               strvec_push(&child.args, "--no-progress");
+
+       if (run_command(&child))
+               return error(_("failed to write multi-pack-index"));
+
+       return 0;
+}
+
+static int multi_pack_index_expire(struct maintenance_run_opts *opts)
+{
+       struct child_process child = CHILD_PROCESS_INIT;
+
+       child.git_cmd = 1;
+       strvec_pushl(&child.args, "multi-pack-index", "expire", NULL);
+
+       if (opts->quiet)
+               strvec_push(&child.args, "--no-progress");
+
+       close_object_store(the_repository->objects);
+
+       if (run_command(&child))
+               return error(_("'git multi-pack-index expire' failed"));
+
+       return 0;
+}
+
+#define TWO_GIGABYTES (INT32_MAX)
+
+static off_t get_auto_pack_size(void)
+{
+       /*
+        * The "auto" value is special: we optimize for
+        * one large pack-file (i.e. from a clone) and
+        * expect the rest to be small and they can be
+        * repacked quickly.
+        *
+        * The strategy we select here is to select a
+        * size that is one more than the second largest
+        * pack-file. This ensures that we will repack
+        * at least two packs if there are three or more
+        * packs.
+        */
+       off_t max_size = 0;
+       off_t second_largest_size = 0;
+       off_t result_size;
+       struct packed_git *p;
+       struct repository *r = the_repository;
+
+       reprepare_packed_git(r);
+       for (p = get_all_packs(r); p; p = p->next) {
+               if (p->pack_size > max_size) {
+                       second_largest_size = max_size;
+                       max_size = p->pack_size;
+               } else if (p->pack_size > second_largest_size)
+                       second_largest_size = p->pack_size;
+       }
+
+       result_size = second_largest_size + 1;
+
+       /* But limit ourselves to a batch size of 2g */
+       if (result_size > TWO_GIGABYTES)
+               result_size = TWO_GIGABYTES;
+
+       return result_size;
+}
+
+static int multi_pack_index_repack(struct maintenance_run_opts *opts)
+{
+       struct child_process child = CHILD_PROCESS_INIT;
+
+       child.git_cmd = 1;
+       strvec_pushl(&child.args, "multi-pack-index", "repack", NULL);
+
+       if (opts->quiet)
+               strvec_push(&child.args, "--no-progress");
+
+       strvec_pushf(&child.args, "--batch-size=%"PRIuMAX,
+                                 (uintmax_t)get_auto_pack_size());
+
+       close_object_store(the_repository->objects);
+
+       if (run_command(&child))
+               return error(_("'git multi-pack-index repack' failed"));
+
+       return 0;
+}
+
+static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts)
+{
+       prepare_repo_settings(the_repository);
+       if (!the_repository->settings.core_multi_pack_index) {
+               warning(_("skipping incremental-repack task because core.multiPackIndex is disabled"));
+               return 0;
+       }
+
+       if (multi_pack_index_write(opts))
+               return 1;
+       if (multi_pack_index_expire(opts))
+               return 1;
+       if (multi_pack_index_repack(opts))
+               return 1;
+       return 0;
+}
+
 typedef int maintenance_task_fn(struct maintenance_run_opts *opts);
 
 /*
@@ -854,6 +1173,9 @@ struct maintenance_task {
 };
 
 enum maintenance_task_label {
+       TASK_PREFETCH,
+       TASK_LOOSE_OBJECTS,
+       TASK_INCREMENTAL_REPACK,
        TASK_GC,
        TASK_COMMIT_GRAPH,
 
@@ -862,6 +1184,20 @@ enum maintenance_task_label {
 };
 
 static struct maintenance_task tasks[] = {
+       [TASK_PREFETCH] = {
+               "prefetch",
+               maintenance_task_prefetch,
+       },
+       [TASK_LOOSE_OBJECTS] = {
+               "loose-objects",
+               maintenance_task_loose_objects,
+               loose_object_auto_condition,
+       },
+       [TASK_INCREMENTAL_REPACK] = {
+               "incremental-repack",
+               maintenance_task_incremental_repack,
+               incremental_repack_auto_condition,
+       },
        [TASK_GC] = {
                "gc",
                maintenance_task_gc,
index c8037388c6e7fd4c62d1a019b1c7b17f020370df..e58e57504c3b5108eb09c9d8198efaa4128ff363 100644 (file)
@@ -670,6 +670,17 @@ static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
                                     NULL, 0);
                obj_read_unlock();
 
+               if (!real_obj) {
+                       char hex[GIT_MAX_HEXSZ + 1];
+                       const char *name = list->objects[i].name;
+
+                       if (!name) {
+                               oid_to_hex_r(hex, &list->objects[i].item->oid);
+                               name = hex;
+                       }
+                       die(_("invalid object '%s' given."), name);
+               }
+
                /* load the gitmodules file for this rev */
                if (recurse_submodules) {
                        submodule_free(opt->repo);
index 0a7ed4bef92b91e75bd8c580544bdd81caea9102..9f939e6cdf2f1048177bed0a5e7ab174f4020ded 100644 (file)
@@ -1738,7 +1738,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                OPT_CALLBACK_F('N', "no-numbered", &numbered, NULL,
                            N_("use [PATCH] even with multiple patches"),
                            PARSE_OPT_NOARG | PARSE_OPT_NONEG, no_numbered_callback),
-               OPT_BOOL('s', "signoff", &do_signoff, N_("add Signed-off-by:")),
+               OPT_BOOL('s', "signoff", &do_signoff, N_("add a Signed-off-by trailer")),
                OPT_BOOL(0, "stdout", &use_stdout,
                            N_("print patches to standard out")),
                OPT_BOOL(0, "cover-letter", &cover_letter,
index e72714a5a87d02540f07fbbc65a86f66ed122cd8..de8520778d2ecdaa01dc23b513e24d187ef89d08 100644 (file)
@@ -109,6 +109,7 @@ static void show_diff(struct merge_list *entry)
        xdemitconf_t xecfg;
        xdemitcb_t ecb;
 
+       memset(&xpp, 0, sizeof(xpp));
        xpp.flags = 0;
        memset(&xecfg, 0, sizeof(xecfg));
        xecfg.ctxlen = 3;
index 9d5359edc2f72799e1bf517db5f26c5f29aff81f..4c133402a62332768b7a7136ded5ea7f4def93fe 100644 (file)
@@ -289,7 +289,7 @@ static struct option builtin_merge_options[] = {
          N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
        OPT_AUTOSTASH(&autostash),
        OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
-       OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
+       OPT_BOOL(0, "signoff", &signoff, N_("add a Signed-off-by trailer")),
        OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
        OPT_END()
 };
index 425950f46973d3949e24cbc1cf8cf74e97eeb417..17aa63cd35aa4425a8832750ecc334e5184ac60d 100644 (file)
@@ -142,7 +142,7 @@ static struct option pull_options[] = {
                N_("add (at most <n>) entries from shortlog to merge commit message"),
                PARSE_OPT_OPTARG),
        OPT_PASSTHRU(0, "signoff", &opt_signoff, NULL,
-               N_("add Signed-off-by:"),
+               N_("add a Signed-off-by trailer"),
                PARSE_OPT_OPTARG),
        OPT_PASSTHRU(0, "squash", &opt_squash, NULL,
                N_("create a single commit instead of doing a merge"),
index 6da3a8e5d30af38530d29c245d15a747dc9f4eed..03adb58602971d8fa754da44d077fd36968b1636 100644 (file)
@@ -290,6 +290,12 @@ static const char message_advice_ref_needs_force[] =
           "or update a remote ref to make it point at a non-commit object,\n"
           "without using the '--force' option.\n");
 
+static const char message_advice_ref_needs_update[] =
+       N_("Updates were rejected because the tip of the remote-tracking\n"
+          "branch has been updated since the last checkout. You may want\n"
+          "to integrate those changes locally (e.g., 'git pull ...')\n"
+          "before forcing an update.\n");
+
 static void advise_pull_before_push(void)
 {
        if (!advice_push_non_ff_current || !advice_push_update_rejected)
@@ -325,6 +331,13 @@ static void advise_ref_needs_force(void)
        advise(_(message_advice_ref_needs_force));
 }
 
+static void advise_ref_needs_update(void)
+{
+       if (!advice_push_ref_needs_update || !advice_push_update_rejected)
+               return;
+       advise(_(message_advice_ref_needs_update));
+}
+
 static int push_with_options(struct transport *transport, struct refspec *rs,
                             int flags)
 {
@@ -374,6 +387,8 @@ static int push_with_options(struct transport *transport, struct refspec *rs,
                advise_ref_fetch_first();
        } else if (reject_reasons & REJECT_NEEDS_FORCE) {
                advise_ref_needs_force();
+       } else if (reject_reasons & REJECT_REF_NEEDS_UPDATE) {
+               advise_ref_needs_update();
        }
 
        return 1;
@@ -510,6 +525,12 @@ static int git_push_config(const char *k, const char *v, void *cb)
                if (!v)
                        return config_error_nonbool(k);
                return color_parse(v, push_colors[slot]);
+       } else if (!strcmp(k, "push.useforceifincludes")) {
+               if (git_config_bool(k, v))
+                       *flags |= TRANSPORT_PUSH_FORCE_IF_INCLUDES;
+               else
+                       *flags &= ~TRANSPORT_PUSH_FORCE_IF_INCLUDES;
+               return 0;
        }
 
        return git_default_config(k, v, NULL);
@@ -541,6 +562,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
                               N_("require old value of ref to be at this value"),
                               PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parseopt_push_cas_option),
+               OPT_BIT(0, TRANS_OPT_FORCE_IF_INCLUDES, &flags,
+                       N_("require remote updates to be integrated locally"),
+                       TRANSPORT_PUSH_FORCE_IF_INCLUDES),
                OPT_CALLBACK(0, "recurse-submodules", &recurse_submodules, "(check|on-demand|no)",
                             N_("control recursive pushing of submodules"), option_parse_recurse_submodules),
                OPT_BOOL_F( 0 , "thin", &thin, N_("use thin pack"), PARSE_OPT_NOCOMPLETE),
@@ -625,6 +649,9 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))
                die(_("--all and --mirror are incompatible"));
 
+       if (!is_empty_cas(&cas) && (flags & TRANSPORT_PUSH_FORCE_IF_INCLUDES))
+               cas.use_force_if_includes = 1;
+
        for_each_string_list_item(item, push_options)
                if (strchr(item->string, '\n'))
                        die(_("push options must not have new line characters"));
index eeca53382f793356e5f4a0114de062fb3926e236..7b65525301c04a0b5f3f20ac8989c9683f2b8d5b 100644 (file)
@@ -1324,7 +1324,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        N_("do not show diffstat of what changed upstream"),
                        PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
                OPT_BOOL(0, "signoff", &options.signoff,
-                        N_("add a Signed-off-by: line to each commit")),
+                        N_("add a Signed-off-by trailer to each commit")),
                OPT_BOOL(0, "committer-date-is-author-date",
                         &options.committer_date_is_author_date,
                         N_("make committer date match author date")),
index 64b4b551eb0259d7972486bfe15c65e91f051073..c1b211b2721c93375b0cc8232bd96db3cdd6ed64 100644 (file)
@@ -191,11 +191,12 @@ static int add(int argc, const char **argv)
        url = argv[1];
 
        remote = remote_get(name);
-       if (remote_is_configured(remote, 1))
-               die(_("remote %s already exists."), name);
+       if (remote_is_configured(remote, 1)) {
+               error(_("remote %s already exists."), name);
+               exit(3);
+       }
 
-       strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name);
-       if (!valid_fetch_refspec(buf2.buf))
+       if (!valid_remote_name(name))
                die(_("'%s' is not a valid remote name"), name);
 
        strbuf_addf(&buf, "remote.%s.url", name);
@@ -686,21 +687,23 @@ static int mv(int argc, const char **argv)
        rename.remote_branches = &remote_branches;
 
        oldremote = remote_get(rename.old_name);
-       if (!remote_is_configured(oldremote, 1))
-               die(_("No such remote: '%s'"), rename.old_name);
+       if (!remote_is_configured(oldremote, 1)) {
+               error(_("No such remote: '%s'"), rename.old_name);
+               exit(2);
+       }
 
        if (!strcmp(rename.old_name, rename.new_name) && oldremote->origin != REMOTE_CONFIG)
                return migrate_file(oldremote);
 
        newremote = remote_get(rename.new_name);
-       if (remote_is_configured(newremote, 1))
-               die(_("remote %s already exists."), rename.new_name);
+       if (remote_is_configured(newremote, 1)) {
+               error(_("remote %s already exists."), rename.new_name);
+               exit(3);
+       }
 
-       strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new_name);
-       if (!valid_fetch_refspec(buf.buf))
+       if (!valid_remote_name(rename.new_name))
                die(_("'%s' is not a valid remote name"), rename.new_name);
 
-       strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s", rename.old_name);
        strbuf_addf(&buf2, "remote.%s", rename.new_name);
        if (git_config_rename_section(buf.buf, buf2.buf) < 1)
@@ -829,8 +832,10 @@ static int rm(int argc, const char **argv)
                usage_with_options(builtin_remote_rm_usage, options);
 
        remote = remote_get(argv[1]);
-       if (!remote_is_configured(remote, 1))
-               die(_("No such remote: '%s'"), argv[1]);
+       if (!remote_is_configured(remote, 1)) {
+               error(_("No such remote: '%s'"), argv[1]);
+               exit(2);
+       }
 
        known_remotes.to_delete = remote;
        for_each_remote(add_known_remote, &known_remotes);
@@ -1511,8 +1516,10 @@ static int set_remote_branches(const char *remotename, const char **branches,
        strbuf_addf(&key, "remote.%s.fetch", remotename);
 
        remote = remote_get(remotename);
-       if (!remote_is_configured(remote, 1))
-               die(_("No such remote '%s'"), remotename);
+       if (!remote_is_configured(remote, 1)) {
+               error(_("No such remote '%s'"), remotename);
+               exit(2);
+       }
 
        if (!add_mode && remove_all_fetch_refspecs(key.buf)) {
                strbuf_release(&key);
@@ -1565,8 +1572,10 @@ static int get_url(int argc, const char **argv)
        remotename = argv[0];
 
        remote = remote_get(remotename);
-       if (!remote_is_configured(remote, 1))
-               die(_("No such remote '%s'"), remotename);
+       if (!remote_is_configured(remote, 1)) {
+               error(_("No such remote '%s'"), remotename);
+               exit(2);
+       }
 
        url_nr = 0;
        if (push_mode) {
@@ -1633,8 +1642,10 @@ static int set_url(int argc, const char **argv)
                oldurl = newurl;
 
        remote = remote_get(remotename);
-       if (!remote_is_configured(remote, 1))
-               die(_("No such remote '%s'"), remotename);
+       if (!remote_is_configured(remote, 1)) {
+               error(_("No such remote '%s'"), remotename);
+               exit(2);
+       }
 
        if (push_mode) {
                strbuf_addf(&name_buf, "remote.%s.pushurl", remotename);
index f61cc5d82cf2697583b5851893ba4076f96643a5..da8997dc861ce37362372659e3eb59dcc694b9d4 100644 (file)
@@ -107,7 +107,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
                OPT_BOOL('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
                OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")),
                OPT_NOOP_NOARG('r', NULL),
-               OPT_BOOL('s', "signoff", &opts->signoff, N_("add Signed-off-by:")),
+               OPT_BOOL('s', "signoff", &opts->signoff, N_("add a Signed-off-by trailer")),
                OPT_CALLBACK('m', "mainline", opts, N_("parent-number"),
                             N_("select mainline parent"), option_parse_m),
                OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
index 7af148d7332fc640f895ff0ff6c387a72680924c..a7e01667b089feed68a8219ae44e721bbcf8ae0b 100644 (file)
@@ -71,6 +71,11 @@ static void print_helper_status(struct ref *ref)
                        msg = "stale info";
                        break;
 
+               case REF_STATUS_REJECT_REMOTE_UPDATED:
+                       res = "error";
+                       msg = "remote ref updated since checkout";
+                       break;
+
                case REF_STATUS_REJECT_ALREADY_EXISTS:
                        res = "error";
                        msg = "already exists";
@@ -173,6 +178,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        int progress = -1;
        int from_stdin = 0;
        struct push_cas_option cas = {0};
+       int force_if_includes = 0;
        struct packet_reader reader;
 
        struct option options[] = {
@@ -198,6 +204,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                OPT_CALLBACK_F(0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"),
                  N_("require old value of ref to be at this value"),
                  PARSE_OPT_OPTARG, parseopt_push_cas_option),
+               OPT_BOOL(0, TRANS_OPT_FORCE_IF_INCLUDES, &force_if_includes,
+                        N_("require remote updates to be integrated locally")),
                OPT_END()
        };
 
@@ -299,6 +307,9 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        if (!is_empty_cas(&cas))
                apply_push_cas(&cas, remote, remote_refs);
 
+       if (!is_empty_cas(&cas) && force_if_includes)
+               cas.use_force_if_includes = 1;
+
        set_ref_status_for_push(remote_refs, args.send_mirror,
                args.force_update);
 
index 99abaeec6c6edcb5a16e0d5e7439981f81561319..ce56fdaaa9c26d938bbb1891d322f02bcfde5ca4 100644 (file)
@@ -676,8 +676,11 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
                } else
                        strbuf_addstr(&sb, "(error)");
        }
-       printf("%s\n", sb.buf);
 
+       if (!is_main_worktree(wt) && worktree_lock_reason(wt))
+               strbuf_addstr(&sb, " locked");
+
+       printf("%s\n", sb.buf);
        strbuf_release(&sb);
 }
 
index cb042bdba8c83288cfc1e42853447d5bc06f47fe..6f62a0731383293eb7e617f0ecda932d421757fd 100644 (file)
@@ -2008,7 +2008,7 @@ static int commit_compare(const void *_a, const void *_b)
 
 static void sort_and_scan_merged_commits(struct write_commit_graph_context *ctx)
 {
-       uint32_t i;
+       uint32_t i, dedup_i = 0;
 
        if (ctx->report_progress)
                ctx->progress = start_delayed_progress(
@@ -2023,17 +2023,27 @@ static void sort_and_scan_merged_commits(struct write_commit_graph_context *ctx)
 
                if (i && oideq(&ctx->commits.list[i - 1]->object.oid,
                          &ctx->commits.list[i]->object.oid)) {
-                       die(_("unexpected duplicate commit id %s"),
-                           oid_to_hex(&ctx->commits.list[i]->object.oid));
+                       /*
+                        * Silently ignore duplicates. These were likely
+                        * created due to a commit appearing in multiple
+                        * layers of the chain, which is unexpected but
+                        * not invalid. We should make sure there is a
+                        * unique copy in the new layer.
+                        */
                } else {
                        unsigned int num_parents;
 
+                       ctx->commits.list[dedup_i] = ctx->commits.list[i];
+                       dedup_i++;
+
                        num_parents = commit_list_count(ctx->commits.list[i]->parents);
                        if (num_parents > 2)
                                ctx->num_extra_edges += num_parents - 1;
                }
        }
 
+       ctx->commits.nr = dedup_i;
+
        stop_progress(&ctx->progress);
 }
 
@@ -2150,6 +2160,11 @@ int write_commit_graph(struct object_directory *odb,
        int replace = 0;
        struct bloom_filter_settings bloom_settings = DEFAULT_BLOOM_FILTER_SETTINGS;
 
+       prepare_repo_settings(the_repository);
+       if (!the_repository->settings.core_commit_graph) {
+               warning(_("attempting to write a commit-graph, but 'core.commitGraph' is disabled"));
+               return 0;
+       }
        if (!commit_graph_compatible(the_repository))
                return 0;
 
index f53429c0ac34d933a06f6051cd6b5e519408e4be..fe1fa3dc41fe787883752671e5954b75616fe9ac 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1586,7 +1586,7 @@ const char *find_commit_header(const char *msg, const char *key, size_t *out_len
 
 /*
  * Inspect the given string and determine the true "end" of the log message, in
- * order to find where to put a new Signed-off-by: line.  Ignored are
+ * order to find where to put a new Signed-off-by trailer.  Ignored are
  * trailing comment lines and blank lines.  To support "git commit -s
  * --amend" on an existing commit, we also ignore "Conflicts:".  To
  * support "git commit -v", we truncate at cut lines.
index 89b218d11a5313f327492795964793bec8670160..3126a5364d08754afce86edf1d0f7cf433cd5051 100644 (file)
@@ -15,6 +15,7 @@ DEVELOPER_CFLAGS += -Wpointer-arith
 DEVELOPER_CFLAGS += -Wstrict-prototypes
 DEVELOPER_CFLAGS += -Wunused
 DEVELOPER_CFLAGS += -Wvla
+DEVELOPER_CFLAGS += -fno-common
 
 ifndef COMPILER_FEATURES
 COMPILER_FEATURES := $(shell ./detect-compiler $(CC))
@@ -45,3 +46,5 @@ ifeq ($(filter gcc5,$(COMPILER_FEATURES)),)
 DEVELOPER_CFLAGS += -Wno-uninitialized
 endif
 endif
+
+GIT_TEST_PERL_FATAL_WARNINGS = YesPlease
index 49a6ef423683f3ee253175b6f2cb28ee2173ed87..7c81e4ba497fb6ab1b9b32a40336f5995b0b5b55 100644 (file)
@@ -1466,14 +1466,15 @@ _git_bundle ()
 # Helper function to decide whether or not we should enable DWIM logic for
 # git-switch and git-checkout.
 #
-# To decide between the following rules in priority order
-# 1) the last provided of "--guess" or "--no-guess" explicitly enable or
-#    disable completion of DWIM logic respectively.
-# 2) If the --no-track option is provided, take this as a hint to disable the
-#    DWIM completion logic
-# 3) If GIT_COMPLETION_CHECKOUT_NO_GUESS is set, disable the DWIM completion
-#    logic, as requested by the user.
-# 4) Enable DWIM logic otherwise.
+# To decide between the following rules in decreasing priority order:
+# - the last provided of "--guess" or "--no-guess" explicitly enable or
+#   disable completion of DWIM logic respectively.
+# - If checkout.guess is false, disable completion of DWIM logic.
+# - If the --no-track option is provided, take this as a hint to disable the
+#   DWIM completion logic
+# - If GIT_COMPLETION_CHECKOUT_NO_GUESS is set, disable the DWIM completion
+#   logic, as requested by the user.
+# - Enable DWIM logic otherwise.
 #
 __git_checkout_default_dwim_mode ()
 {
@@ -1484,11 +1485,17 @@ __git_checkout_default_dwim_mode ()
        fi
 
        # --no-track disables DWIM, but with lower priority than
-       # --guess/--no-guess
+       # --guess/--no-guess/checkout.guess
        if [ -n "$(__git_find_on_cmdline "--no-track")" ]; then
                dwim_opt=""
        fi
 
+       # checkout.guess = false disables DWIM, but with lower priority than
+       # --guess/--no-guess
+       if [ "$(__git config --type=bool checkout.guess)" = "false" ]; then
+               dwim_opt=""
+       fi
+
        # Find the last provided --guess or --no-guess
        last_option="$(__git_find_last_on_cmdline "--guess --no-guess")"
        case "$last_option" in
@@ -1687,8 +1694,13 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
                        --submodule --submodule= --ignore-submodules
                        --indent-heuristic --no-indent-heuristic
                        --textconv --no-textconv
+                       --patch --no-patch
 "
 
+__git_diff_difftool_options="--cached --staged --pickaxe-all --pickaxe-regex
+                       --base --ours --theirs --no-index --relative --merge-base
+                       $__git_diff_common_options"
+
 _git_diff ()
 {
        __git_has_doubledash && return
@@ -1711,10 +1723,7 @@ _git_diff ()
                return
                ;;
        --*)
-               __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
-                       --base --ours --theirs --no-index
-                       $__git_diff_common_options
-                       "
+               __gitcomp "$__git_diff_difftool_options"
                return
                ;;
        esac
@@ -1736,11 +1745,7 @@ _git_difftool ()
                return
                ;;
        --*)
-               __gitcomp_builtin difftool "$__git_diff_common_options
-                                       --base --cached --ours --theirs
-                                       --pickaxe-all --pickaxe-regex
-                                       --relative --staged
-                                       "
+               __gitcomp_builtin difftool "$__git_diff_difftool_options"
                return
                ;;
        esac
@@ -2030,11 +2035,9 @@ _git_log ()
                        --no-walk --no-walk= --do-walk
                        --parents --children
                        --expand-tabs --expand-tabs= --no-expand-tabs
-                       --patch
                        $merge
                        $__git_diff_common_options
                        --pickaxe-all --pickaxe-regex
-                       --patch --no-patch
                        "
                return
                ;;
@@ -2937,7 +2940,7 @@ _git_show ()
                ;;
        --*)
                __gitcomp "--pretty= --format= --abbrev-commit --no-abbrev-commit
-                       --oneline --show-signature --patch
+                       --oneline --show-signature
                        --expand-tabs --expand-tabs= --no-expand-tabs
                        $__git_diff_common_options
                        "
@@ -3020,7 +3023,10 @@ _git_stash ()
                list,--*)
                        __gitcomp "--name-status --oneline --patch-with-stat"
                        ;;
-               show,--*|branch,--*)
+               show,--*)
+                       __gitcomp "$__git_diff_common_options"
+                       ;;
+               branch,--*)
                        ;;
                branch,*)
                        if [ $cword -eq 3 ]; then
index 8c171dd959f69501706c565218665ef474252d43..d843df3afd0ec490102860c57621a6bc7295abdc 100755 (executable)
@@ -27,7 +27,7 @@ n,dry-run            don't recreate the branch"
 
 search_reflog () {
        sed -ne 's~^\([^ ]*\) .*        checkout: moving from '"$1"' .*~\1~p' \
-                < "$GIT_DIR"/logs/HEAD
+               < "$GIT_DIR"/logs/HEAD
 }
 
 search_reflog_merges () {
@@ -37,19 +37,18 @@ search_reflog_merges () {
        )
 }
 
-_x40="[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]"
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+oid_pattern=$(git hash-object --stdin </dev/null | sed -e 's/./[0-9a-f]/g')
 
 search_merges () {
-        git rev-list --all --grep="Merge branch '$1'" \
-                --pretty=tformat:"%P %s" |
-        sed -ne "/^$_x40 \($_x40\) Merge .*/ {s//\1/p;$early_exit}"
+       git rev-list --all --grep="Merge branch '$1'" \
+               --pretty=tformat:"%P %s" |
+       sed -ne "/^$oid_pattern \($oid_pattern\) Merge .*/ {s//\1/p;$early_exit}"
 }
 
 search_merge_targets () {
        git rev-list --all --grep="Merge branch '[^']*' into $branch\$" \
                --pretty=tformat:"%H %s" --all |
-       sed -ne "/^\($_x40\) Merge .*/ {s//\1/p;$early_exit} "
+       sed -ne "/^\($oid_pattern\) Merge .*/ {s//\1/p;$early_exit} "
 }
 
 dry_run=
index f95c6de75fc843fd19f683b6217c5fe87f341cd1..082e249fc3c676f2fbcc28eef5db838ecea2b59d 100644 (file)
@@ -13,6 +13,7 @@
 #include "submodule.h"
 #include "dir.h"
 #include "fsmonitor.h"
+#include "commit-reach.h"
 
 /*
  * diff-files
@@ -97,6 +98,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
 
        diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/");
 
+       refresh_fsmonitor(istate);
+
        if (diff_unmerged_stage < 0)
                diff_unmerged_stage = 2;
        entries = istate->cache_nr;
@@ -197,8 +200,17 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                if (ce_uptodate(ce) || ce_skip_worktree(ce))
                        continue;
 
-               /* If CE_VALID is set, don't look at workdir for file removal */
-               if (ce->ce_flags & CE_VALID) {
+               /*
+                * When CE_VALID is set (via "update-index --assume-unchanged"
+                * or via adding paths while core.ignorestat is set to true),
+                * the user has promised that the working tree file for that
+                * path will not be modified.  When CE_FSMONITOR_VALID is true,
+                * the fsmonitor knows that the path hasn't been modified since
+                * we refreshed the cached stat information.  In either case,
+                * we do not have to stat to see if the path has been removed
+                * or modified.
+                */
+               if (ce->ce_flags & (CE_VALID | CE_FSMONITOR_VALID)) {
                        changed = 0;
                        newmode = ce->ce_mode;
                } else {
@@ -510,16 +522,74 @@ static int diff_cache(struct rev_info *revs,
        return unpack_trees(1, &t, &opts);
 }
 
-int run_diff_index(struct rev_info *revs, int cached)
+void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb)
+{
+       int i;
+       struct commit *mb_child[2] = {0};
+       struct commit_list *merge_bases;
+
+       for (i = 0; i < revs->pending.nr; i++) {
+               struct object *obj = revs->pending.objects[i].item;
+               if (obj->flags)
+                       die(_("--merge-base does not work with ranges"));
+               if (obj->type != OBJ_COMMIT)
+                       die(_("--merge-base only works with commits"));
+       }
+
+       /*
+        * This check must go after the for loop above because A...B
+        * ranges produce three pending commits, resulting in a
+        * misleading error message.
+        */
+       if (revs->pending.nr < 1 || revs->pending.nr > 2)
+               BUG("unexpected revs->pending.nr: %d", revs->pending.nr);
+
+       for (i = 0; i < revs->pending.nr; i++)
+               mb_child[i] = lookup_commit_reference(the_repository, &revs->pending.objects[i].item->oid);
+       if (revs->pending.nr == 1) {
+               struct object_id oid;
+
+               if (get_oid("HEAD", &oid))
+                       die(_("unable to get HEAD"));
+
+               mb_child[1] = lookup_commit_reference(the_repository, &oid);
+       }
+
+       merge_bases = repo_get_merge_bases(the_repository, mb_child[0], mb_child[1]);
+       if (!merge_bases)
+               die(_("no merge base found"));
+       if (merge_bases->next)
+               die(_("multiple merge bases found"));
+
+       oidcpy(mb, &merge_bases->item->object.oid);
+
+       free_commit_list(merge_bases);
+}
+
+int run_diff_index(struct rev_info *revs, unsigned int option)
 {
        struct object_array_entry *ent;
+       int cached = !!(option & DIFF_INDEX_CACHED);
+       int merge_base = !!(option & DIFF_INDEX_MERGE_BASE);
+       struct object_id oid;
+       const char *name;
+       char merge_base_hex[GIT_MAX_HEXSZ + 1];
 
        if (revs->pending.nr != 1)
                BUG("run_diff_index must be passed exactly one tree");
 
        trace_performance_enter();
        ent = revs->pending.objects;
-       if (diff_cache(revs, &ent->item->oid, ent->name, cached))
+
+       if (merge_base) {
+               diff_get_merge_base(revs, &oid);
+               name = oid_to_hex_r(merge_base_hex, &oid);
+       } else {
+               oidcpy(&oid, &ent->item->oid);
+               name = ent->name;
+       }
+
+       if (diff_cache(revs, &oid, name, cached))
                exit(128);
 
        diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/");
diff --git a/diff.c b/diff.c
index 2bb2f8f57e8b7ce5a6d861b7a1d598a7fbaf68e9..d24f47df993ead73df360571a6ad9cbac604287f 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -3587,6 +3587,8 @@ static void builtin_diff(const char *name_a,
                if (header.len && !o->flags.suppress_diff_headers)
                        ecbdata.header = &header;
                xpp.flags = o->xdl_opts;
+               xpp.ignore_regex = o->ignore_regex;
+               xpp.ignore_regex_nr = o->ignore_regex_nr;
                xpp.anchors = o->anchors;
                xpp.anchors_nr = o->anchors_nr;
                xecfg.ctxlen = o->context;
@@ -3716,6 +3718,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
                memset(&xpp, 0, sizeof(xpp));
                memset(&xecfg, 0, sizeof(xecfg));
                xpp.flags = o->xdl_opts;
+               xpp.ignore_regex = o->ignore_regex;
+               xpp.ignore_regex_nr = o->ignore_regex_nr;
                xpp.anchors = o->anchors;
                xpp.anchors_nr = o->anchors_nr;
                xecfg.ctxlen = o->context;
@@ -5203,6 +5207,22 @@ static int diff_opt_patience(const struct option *opt,
        return 0;
 }
 
+static int diff_opt_ignore_regex(const struct option *opt,
+                                const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+       regex_t *regex;
+
+       BUG_ON_OPT_NEG(unset);
+       regex = xmalloc(sizeof(*regex));
+       if (regcomp(regex, arg, REG_EXTENDED | REG_NEWLINE))
+               return error(_("invalid regex given to -I: '%s'"), arg);
+       ALLOC_GROW(options->ignore_regex, options->ignore_regex_nr + 1,
+                  options->ignore_regex_alloc);
+       options->ignore_regex[options->ignore_regex_nr++] = regex;
+       return 0;
+}
+
 static int diff_opt_pickaxe_regex(const struct option *opt,
                                  const char *arg, int unset)
 {
@@ -5491,6 +5511,9 @@ static void prep_parse_options(struct diff_options *options)
                OPT_BIT_F(0, "ignore-blank-lines", &options->xdl_opts,
                          N_("ignore changes whose lines are all blank"),
                          XDF_IGNORE_BLANK_LINES, PARSE_OPT_NONEG),
+               OPT_CALLBACK_F('I', "ignore-matching-lines", options, N_("<regex>"),
+                              N_("ignore changes whose all lines match <regex>"),
+                              0, diff_opt_ignore_regex),
                OPT_BIT(0, "indent-heuristic", &options->xdl_opts,
                        N_("heuristic to shift diff hunk boundaries for easy reading"),
                        XDF_INDENT_HEURISTIC),
diff --git a/diff.h b/diff.h
index 11de52e9e9b0cc67704b56d6579970c95bc28cd7..9665e220063cd9543528c64b17f57dc761bad028 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -234,6 +234,10 @@ struct diff_options {
         */
        const char *pickaxe;
 
+       /* -I<regex> */
+       regex_t **ignore_regex;
+       size_t ignore_regex_nr, ignore_regex_alloc;
+
        const char *single_follow;
        const char *a_prefix, *b_prefix;
        const char *line_prefix;
@@ -578,12 +582,17 @@ void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc);
  */
 const char *diff_aligned_abbrev(const struct object_id *sha1, int);
 
+void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb);
+
 /* do not report anything on removed paths */
 #define DIFF_SILENT_ON_REMOVED 01
 /* report racily-clean paths as modified */
 #define DIFF_RACY_IS_MODIFIED 02
 int run_diff_files(struct rev_info *revs, unsigned int option);
-int run_diff_index(struct rev_info *revs, int cached);
+
+#define DIFF_INDEX_CACHED 01
+#define DIFF_INDEX_MERGE_BASE 02
+int run_diff_index(struct rev_info *revs, unsigned int option);
 
 int do_diff_cache(const struct object_id *, struct diff_options *);
 int diff_flush_patch_id(struct diff_options *, struct object_id *, int, int);
diff --git a/dir.c b/dir.c
index 78387110e609e16d769b4c42bcba1d1effb731e0..ebea5f1f91439a097e48ffb5983fbff2bc0ff918 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -1040,9 +1040,9 @@ static int add_patterns_from_buffer(char *buf, size_t size,
  * an index if 'istate' is non-null), parse it and store the
  * exclude rules in "pl".
  *
- * If "ss" is not NULL, compute SHA-1 of the exclude file and fill
+ * If "oid_stat" is not NULL, compute oid of the exclude file and fill
  * stat data from disk (only valid if add_patterns returns zero). If
- * ss_valid is non-zero, "ss" must contain good value as input.
+ * oid_stat.valid is non-zero, "oid_stat" must contain good value as input.
  */
 static int add_patterns(const char *fname, const char *base, int baselen,
                        struct pattern_list *pl, struct index_state *istate,
@@ -1090,7 +1090,7 @@ static int add_patterns(const char *fname, const char *base, int baselen,
                        int pos;
                        if (oid_stat->valid &&
                            !match_stat_data_racy(istate, &oid_stat->stat, &st))
-                               ; /* no content change, ss->sha1 still good */
+                               ; /* no content change, oid_stat->oid still good */
                        else if (istate &&
                                 (pos = index_name_pos(istate, fname, strlen(fname))) >= 0 &&
                                 !ce_stage(istate->cache[pos]) &&
index bd22e1ea8865c086dd5e0c7fa32229036c1d83f3..9a664a4a5832daba2a69e1d91d996eddb1f8bb56 100644 (file)
@@ -626,8 +626,10 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
        void *current_branch_to_free;
        struct merge_parents merge_parents;
 
-       if (!suppress_dest_pattern_seen)
+       if (!suppress_dest_pattern_seen) {
+               string_list_append(&suppress_dest_patterns, "main");
                string_list_append(&suppress_dest_patterns, "master");
+       }
 
        memset(&merge_parents, 0, sizeof(merge_parents));
 
index 8a720187125798e7e18d145a68210f0d9e6780f7..e713fe3d022effd7905317b25d26bc6ab84914c5 100755 (executable)
@@ -1830,6 +1830,13 @@ sub process_args {
                                $arg = shift @ARGV or die __("missing --");
                                if ($arg ne '--') {
                                        $patch_mode_revision = $arg;
+
+                                       # NEEDSWORK: Instead of comparing to the literal "HEAD",
+                                       # compare the commit objects instead so that other ways of
+                                       # saying the same thing (such as "@") are also handled
+                                       # appropriately.
+                                       #
+                                       # This applies to the cases below too.
                                        $patch_mode = ($arg eq 'HEAD' ?
                                                       'reset_head' : 'reset_nothead');
                                        $arg = shift @ARGV or die __("missing --");
index ea7e684ebb55481b8118adaab6dd2324cf7bf3e8..1f3f6e9fc5684d3e9dd2a0ac0260556955c9fa4b 100755 (executable)
@@ -39,37 +39,6 @@ _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 TERM_BAD=bad
 TERM_GOOD=good
 
-bisect_head()
-{
-       if git rev-parse --verify -q BISECT_HEAD > /dev/null
-       then
-               echo BISECT_HEAD
-       else
-               echo HEAD
-       fi
-}
-
-bisect_start() {
-       git bisect--helper --bisect-start $@ || exit
-
-       #
-       # Change state.
-       # In case of mistaken revs or checkout error, or signals received,
-       # "bisect_auto_next" below may exit or misbehave.
-       # We have to trap this to be able to clean up using
-       # "bisect_clean_state".
-       #
-       trap 'git bisect--helper --bisect-clean-state' 0
-       trap 'exit 255' 1 2 3 15
-
-       #
-       # Check if we can proceed to the next bisect state.
-       #
-       git bisect--helper --bisect-auto-next || exit
-
-       trap '-' 0
-}
-
 bisect_skip() {
        all=''
        for arg in "$@"
@@ -82,43 +51,7 @@ bisect_skip() {
                esac
                all="$all $revs"
        done
-       eval bisect_state 'skip' $all
-}
-
-bisect_state() {
-       git bisect--helper --bisect-autostart || exit
-       state=$1
-       git bisect--helper --check-and-set-terms $state $TERM_GOOD $TERM_BAD || exit
-       get_terms
-       case "$#,$state" in
-       0,*)
-               die "Please call 'bisect_state' with at least one argument." ;;
-       1,"$TERM_BAD"|1,"$TERM_GOOD"|1,skip)
-               bisected_head=$(bisect_head)
-               rev=$(git rev-parse --verify "$bisected_head") ||
-                       die "$(eval_gettext "Bad rev input: \$bisected_head")"
-               git bisect--helper --bisect-write "$state" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit
-               git bisect--helper --check-expected-revs "$rev" ;;
-       2,"$TERM_BAD"|*,"$TERM_GOOD"|*,skip)
-               shift
-               hash_list=''
-               for rev in "$@"
-               do
-                       sha=$(git rev-parse --verify "$rev^{commit}") ||
-                               die "$(eval_gettext "Bad rev input: \$rev")"
-                       hash_list="$hash_list $sha"
-               done
-               for rev in $hash_list
-               do
-                       git bisect--helper --bisect-write "$state" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit
-               done
-               git bisect--helper --check-expected-revs $hash_list ;;
-       *,"$TERM_BAD")
-               die "$(eval_gettext "'git bisect \$TERM_BAD' can take only one argument.")" ;;
-       *)
-               usage ;;
-       esac
-       git bisect--helper --bisect-auto-next
+       eval git bisect--helper --bisect-state 'skip' $all
 }
 
 bisect_visualize() {
@@ -163,8 +96,7 @@ bisect_replay () {
                get_terms
                case "$command" in
                start)
-                       cmd="bisect_start $rev $tail"
-                       eval "$cmd" ;;
+                       eval "git bisect--helper --bisect-start $rev $tail" ;;
                "$TERM_GOOD"|"$TERM_BAD"|skip)
                        git bisect--helper --bisect-write "$command" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit;;
                terms)
@@ -209,8 +141,7 @@ exit code \$res from '\$command' is < 0 or >= 128" >&2
                        state="$TERM_GOOD"
                fi
 
-               # We have to use a subshell because "bisect_state" can exit.
-               ( bisect_state $state >"$GIT_DIR/BISECT_RUN" )
+               git bisect--helper --bisect-state $state >"$GIT_DIR/BISECT_RUN"
                res=$?
 
                cat "$GIT_DIR/BISECT_RUN"
@@ -225,7 +156,7 @@ exit code \$res from '\$command' is < 0 or >= 128" >&2
                if [ $res -ne 0 ]
                then
                        eval_gettextln "bisect run failed:
-'bisect_state \$state' exited with error code \$res" >&2
+'bisect-state \$state' exited with error code \$res" >&2
                        exit $res
                fi
 
@@ -264,9 +195,9 @@ case "$#" in
        help)
                git bisect -h ;;
        start)
-               bisect_start "$@" ;;
+               git bisect--helper --bisect-start "$@" ;;
        bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD")
-               bisect_state "$cmd" "$@" ;;
+               git bisect--helper --bisect-state "$cmd" "$@" ;;
        skip)
                bisect_skip "$@" ;;
        next)
index 7a0fb7a04597a4b6fa7ea48194aa334852104eb9..adfea068970a2f62a66293c0b1de45bad05b5114 100644 (file)
@@ -489,11 +489,13 @@ static inline int const_error(void)
 #define error_errno(...) (error_errno(__VA_ARGS__), const_error())
 #endif
 
-void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
-void set_error_routine(void (*routine)(const char *err, va_list params));
-extern void (*get_error_routine(void))(const char *err, va_list params);
-void set_warn_routine(void (*routine)(const char *warn, va_list params));
-extern void (*get_warn_routine(void))(const char *warn, va_list params);
+typedef void (*report_fn)(const char *, va_list params);
+
+void set_die_routine(NORETURN_PTR report_fn routine);
+void set_error_routine(report_fn routine);
+report_fn get_error_routine(void);
+void set_warn_routine(report_fn routine);
+report_fn get_warn_routine(void);
 void set_die_is_recursing_routine(int (*routine)(void));
 
 int starts_with(const char *str, const char *prefix);
index 58f5a7ac8f80ac609fe1b35a558f936c7af42b98..70cb5e2a83b5bec6e9f95ea0cb7feb112e2dfec5 100755 (executable)
@@ -2,7 +2,7 @@
 # Copyright (C) 2006, Eric Wong <normalperson@yhbt.net>
 # License: GPL v2 or later
 use 5.008;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use strict;
 use vars qw/   $AUTHOR $VERSION
                $oid $oid_short $oid_length
index 68eeb425f8adff845409d2f0f7f64118d97593a2..75c8b1acfff86508614321a83989e2940fc21d4b 100644 (file)
@@ -481,7 +481,7 @@ static struct commit *check_single_commit(struct rev_info *revs)
                if (obj->flags & UNINTERESTING)
                        continue;
                obj = deref_tag(revs->repo, obj, NULL, 0);
-               if (obj->type != OBJ_COMMIT)
+               if (!obj || obj->type != OBJ_COMMIT)
                        die("Non commit %s?", revs->pending.objects[i].name);
                if (commit)
                        die("More than one commit to dig from: %s and %s?",
diff --git a/midx.c b/midx.c
index 0de42ffdfb22c630e5b28f4997aa558c01a44d79..d233b54ac71a0a81fea31ab0286025802a3ed240 100644 (file)
--- a/midx.c
+++ b/midx.c
@@ -10,6 +10,7 @@
 #include "progress.h"
 #include "trace2.h"
 #include "run-command.h"
+#include "repository.h"
 
 #define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
 #define MIDX_VERSION 1
@@ -398,15 +399,9 @@ int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, i
 {
        struct multi_pack_index *m;
        struct multi_pack_index *m_search;
-       int config_value;
-       static int env_value = -1;
 
-       if (env_value < 0)
-               env_value = git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0);
-
-       if (!env_value &&
-           (repo_config_get_bool(r, "core.multipackindex", &config_value) ||
-           !config_value))
+       prepare_repo_settings(r);
+       if (!r->settings.core_multi_pack_index)
                return 0;
 
        for (m_search = r->objects->multi_pack_index; m_search; m_search = m_search->next)
@@ -850,7 +845,7 @@ static int write_midx_internal(const char *object_dir, struct multi_pack_index *
 
        packs.pack_paths_checked = 0;
        if (flags & MIDX_PROGRESS)
-               packs.progress = start_progress(_("Adding packfiles to multi-pack-index"), 0);
+               packs.progress = start_delayed_progress(_("Adding packfiles to multi-pack-index"), 0);
        else
                packs.progress = NULL;
 
@@ -987,7 +982,7 @@ static int write_midx_internal(const char *object_dir, struct multi_pack_index *
        }
 
        if (flags & MIDX_PROGRESS)
-               progress = start_progress(_("Writing chunks to multi-pack-index"),
+               progress = start_delayed_progress(_("Writing chunks to multi-pack-index"),
                                          num_chunks);
        for (i = 0; i < num_chunks; i++) {
                if (written != chunk_offsets[i])
@@ -1129,7 +1124,7 @@ int verify_midx_file(struct repository *r, const char *object_dir, unsigned flag
        }
 
        if (flags & MIDX_PROGRESS)
-               progress = start_progress(_("Looking for referenced packfiles"),
+               progress = start_delayed_progress(_("Looking for referenced packfiles"),
                                          m->num_packs);
        for (i = 0; i < m->num_packs; i++) {
                if (prepare_midx_pack(r, m, i))
@@ -1250,7 +1245,7 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla
        count = xcalloc(m->num_packs, sizeof(uint32_t));
 
        if (flags & MIDX_PROGRESS)
-               progress = start_progress(_("Counting referenced objects"),
+               progress = start_delayed_progress(_("Counting referenced objects"),
                                          m->num_objects);
        for (i = 0; i < m->num_objects; i++) {
                int pack_int_id = nth_midxed_pack_int_id(m, i);
@@ -1260,7 +1255,7 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla
        stop_progress(&progress);
 
        if (flags & MIDX_PROGRESS)
-               progress = start_progress(_("Finding and deleting unreferenced packfiles"),
+               progress = start_delayed_progress(_("Finding and deleting unreferenced packfiles"),
                                          m->num_packs);
        for (i = 0; i < m->num_packs; i++) {
                char *pack_name;
index 8b95e2d73d0f8f8d5b50eaaa360135f102b52b74..d82b71325c6bd492cd7eb0f703b00fa6634c627d 100644 (file)
@@ -12,7 +12,7 @@
 package Error;
 
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 
 use vars qw($VERSION);
 use 5.004;
index 10df990959e63938e1742d1cdaf0d009b61bee96..02eacef0c2a4a43058a74dda4cebe1ae16bdd2eb 100644 (file)
@@ -9,7 +9,7 @@ package Git;
 
 use 5.008;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 
 use File::Temp ();
 use File::Spec ();
index bfb4fb67a13f4530aae2d974e579b9ba45e20cdb..2037f387c89e8a8769910e164174085f1d2e645c 100644 (file)
@@ -1,7 +1,7 @@
 package Git::I18N;
 use 5.008;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 BEGIN {
        require Exporter;
        if ($] < 5.008003) {
index 2a7b4908f3e637d3e93462799c91cd75a8221354..9ee054f854e8682e16933ad0a770be5847bd33e5 100644 (file)
@@ -1,6 +1,6 @@
 package Git::IndexInfo;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Git qw/command_input_pipe command_close_pipe/;
 
 sub new {
index e5585e75e809ef5763d3d4f19ebd185c6f7c0f51..0c360bc799860798ade6b2afce6e05b0a567d21f 100644 (file)
@@ -1,7 +1,7 @@
 package Git::LoadCPAN;
 use 5.008;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 
 =head1 NAME
 
index c6d2c45d808b2ec553f53f15ad5f495844199ac0..5d84c202884b7cb7cea576ea1a6f146b2e653ae2 100644 (file)
@@ -1,7 +1,7 @@
 package Git::LoadCPAN::Error;
 use 5.008;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Git::LoadCPAN (
        module => 'Error',
        import => 1,
index f70a4f064c3c78a87be3f876ea3f325d6b6fa4de..340e88a7a56be37af8b1f437c92ecb52b1b29089 100644 (file)
@@ -1,7 +1,7 @@
 package Git::LoadCPAN::Mail::Address;
 use 5.008;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Git::LoadCPAN (
        module => 'Mail::Address',
        import => 0,
index b75738bed4b52ac0085eb6a4919ba954d8fcfe98..d144f5168f37adaf54a2f9b768f04ad2a1c20196 100644 (file)
@@ -1,7 +1,7 @@
 package Git::Packet;
 use 5.008;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 BEGIN {
        require Exporter;
        if ($] < 5.008003) {
index d1c352f92b58392961b8e958c5539cba65458856..f6f1dc03c60861ca1fba1faf66617ad8b6c1b8b4 100644 (file)
@@ -1,6 +1,6 @@
 package Git::SVN;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Fcntl qw/:DEFAULT :seek/;
 use constant rev_map_fmt => 'NH*';
 use vars qw/$_no_metadata
index c961444d4cbb622ea8045587c7294d50f2b655ef..47fd048bf2d14e3274ed0b6f7d3dbe957ac75b3c 100644 (file)
@@ -1,7 +1,7 @@
 package Git::SVN::Editor;
 use vars qw/@ISA $_rmdir $_cp_similarity $_find_copies_harder $_rename_limit/;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use SVN::Core;
 use SVN::Delta;
 use Carp qw/croak/;
index 729e5337df7accad3340f3a8f7411ba0ffd31085..968309e6d63b904c6ed76d78ee3f60bdfe64e271 100644 (file)
@@ -3,7 +3,7 @@ use vars qw/@ISA $_ignore_regex $_include_regex $_preserve_empty_dirs
             $_placeholder_filename @deleted_gpath %added_placeholder
             $repo_id/;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use SVN::Delta;
 use Carp qw/croak/;
 use File::Basename qw/dirname/;
index a0a8d1762150acd5b6a91116d4835fba1834d425..f2c1e1f6fbe8899cd3f871d4fefcee2c5939f2a5 100644 (file)
@@ -1,6 +1,6 @@
 package Git::SVN::GlobSpec;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 
 sub new {
        my ($class, $glob, $pattern_ok) = @_;
index 3858fcf27dee85c8d2b72ff96402d253c8ea1772..cc8b484f848028e397fb5030f73b0d875a5602c9 100644 (file)
@@ -1,6 +1,6 @@
 package Git::SVN::Log;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Git::SVN::Utils qw(fatal);
 use Git qw(command
            command_oneline
@@ -298,7 +298,7 @@ sub cmd_show_log {
                        get_author_info($c, $1, $2, $3);
                } elsif (/^${esc_color}(?:tree|parent|committer) /o) {
                        # ignore
-               } elsif (/^${esc_color}:\d{6} \d{6} $::sha1_short/o) {
+               } elsif (/^${esc_color}:\d{6} \d{6} $::oid_short/o) {
                        push @{$c->{raw}}, $_;
                } elsif (/^${esc_color}[ACRMDT]\t/) {
                        # we could add $SVN->{svn_path} here, but that requires
index 9676b8f2f73520735b68f6ff13d03d52575afaae..8fcf6644a1c198ce7ea4d67d490f4a975251bf86 100644 (file)
@@ -1,5 +1,5 @@
 package Git::SVN::Memoize::YAML;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use strict;
 use YAML::Any ();
 
index dc90f6a6214281386c401235827a8fd268994eeb..ed96ac593e8098fcdf93a8a7b9639ae1a2734c73 100644 (file)
@@ -33,7 +33,7 @@ package Git::SVN::Migration;
 #              possible if noMetadata or useSvmProps are set; but should
 #              be no problem for users that use the (sensible) defaults.
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Carp qw/croak/;
 use File::Path qw/mkpath/;
 use File::Basename qw/dirname basename/;
index e940b08505f1488efbe257201b3881b7e00a0a2a..de158e848fb0be0e3c7b8239cc774f695cf3ec7c 100644 (file)
@@ -1,6 +1,6 @@
 package Git::SVN::Prompt;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 require SVN::Core;
 use vars qw/$_no_auth_cache $_username/;
 
index 2cfe055a9a04305ab5b248a6de0bab2a8abfcec0..912e035040448858e0a53ecdd72deb4bf11206ab 100644 (file)
@@ -1,7 +1,7 @@
 package Git::SVN::Ra;
 use vars qw/@ISA $config_dir $_ignore_refs_regex $_log_window_size/;
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 use Memoize;
 use Git::SVN::Utils qw(
        canonicalize_url
index 3d1a0933a2eb27dc123730c67470be503e91bf93..5ca09ab448328b7e29c1cc248191cd8abf2a82f3 100644 (file)
@@ -1,7 +1,7 @@
 package Git::SVN::Utils;
 
 use strict;
-use warnings;
+use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
 
 use SVN::Core;
 
index 844c253ccd9a9ee770fe494a0cce84f53dc8fed8..d633005ef746ad1eb7445cb75401e0c3f55a2312 100644 (file)
@@ -461,9 +461,11 @@ int recv_sideband(const char *me, int in_stream, int out)
        enum sideband_type sideband_type;
 
        while (1) {
-               len = packet_read(in_stream, NULL, NULL, buf, LARGE_PACKET_MAX,
-                                 0);
-               if (!demultiplex_sideband(me, buf, len, 0, &scratch,
+               int status = packet_read_with_status(in_stream, NULL, NULL,
+                                                    buf, LARGE_PACKET_MAX,
+                                                    &len,
+                                                    PACKET_READ_GENTLE_ON_EOF);
+               if (!demultiplex_sideband(me, status, buf, len, 0, &scratch,
                                          &sideband_type))
                        continue;
                switch (sideband_type) {
@@ -471,6 +473,9 @@ int recv_sideband(const char *me, int in_stream, int out)
                        write_or_die(out, buf + 1, len - 1);
                        break;
                default: /* errors: message already written */
+                       if (scratch.len > 0)
+                               BUG("unhandled incomplete sideband: '%s'",
+                                   scratch.buf);
                        return sideband_type;
                }
        }
@@ -517,9 +522,9 @@ enum packet_read_status packet_reader_read(struct packet_reader *reader)
                                                         reader->options);
                if (!reader->use_sideband)
                        break;
-               if (demultiplex_sideband(reader->me, reader->buffer,
-                                        reader->pktlen, 1, &scratch,
-                                        &sideband_type))
+               if (demultiplex_sideband(reader->me, reader->status,
+                                        reader->buffer, reader->pktlen, 1,
+                                        &scratch, &sideband_type))
                        break;
        }
 
index c62f6b482271e124474aab6984820dcdd2db698c..6476686fea1d03d9cc43d5c03f7c0b3fe3052030 100644 (file)
@@ -1097,14 +1097,19 @@ static const char *copy_email(const char *buf, struct used_atom *atom)
 
 static char *copy_subject(const char *buf, unsigned long len)
 {
-       char *r = xmemdupz(buf, len);
+       struct strbuf sb = STRBUF_INIT;
        int i;
 
-       for (i = 0; i < len; i++)
-               if (r[i] == '\n')
-                       r[i] = ' ';
+       for (i = 0; i < len; i++) {
+               if (buf[i] == '\r' && i + 1 < len && buf[i + 1] == '\n')
+                       continue; /* ignore CR in CRLF */
 
-       return r;
+               if (buf[i] == '\n')
+                       strbuf_addch(&sb, ' ');
+               else
+                       strbuf_addch(&sb, buf[i]);
+       }
+       return strbuf_detach(&sb, NULL);
 }
 
 static void grab_date(const char *buf, struct atom_value *v, const char *atomname)
@@ -1228,20 +1233,23 @@ static void find_subpos(const char *buf,
 
        /* subject is first non-empty line */
        *sub = buf;
-       /* subject goes to first empty line */
-       while (buf < *sig && *buf && *buf != '\n') {
-               eol = strchrnul(buf, '\n');
-               if (*eol)
-                       eol++;
-               buf = eol;
-       }
+       /* subject goes to first empty line before signature begins */
+       if ((eol = strstr(*sub, "\n\n"))) {
+               eol = eol < *sig ? eol : *sig;
+       /* check if message uses CRLF */
+       } else if (! (eol = strstr(*sub, "\r\n\r\n"))) {
+               /* treat whole message as subject */
+               eol = strrchr(*sub, '\0');
+       }
+       buf = eol;
        *sublen = buf - *sub;
        /* drop trailing newline, if present */
-       if (*sublen && (*sub)[*sublen - 1] == '\n')
+       while (*sublen && ((*sub)[*sublen - 1] == '\n' ||
+                          (*sub)[*sublen - 1] == '\r'))
                *sublen -= 1;
 
        /* skip any empty lines */
-       while (*buf == '\n')
+       while (*buf == '\n' || *buf == '\r')
                buf++;
        *body = buf;
        *bodylen = strlen(buf);
diff --git a/refs.c b/refs.c
index fa01153151a8c66c18da4020a42f93063c835cdf..392f0bbf68b14926a81aa248ca7af3e43d5215eb 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -567,8 +567,11 @@ char *repo_default_branch_name(struct repository *r)
        const char *config_key = "init.defaultbranch";
        const char *config_display_key = "init.defaultBranch";
        char *ret = NULL, *full_ref;
+       const char *env = getenv("GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME");
 
-       if (repo_config_get_string(r, config_key, &ret) < 0)
+       if (env && *env)
+               ret = xstrdup(env);
+       else if (repo_config_get_string(r, config_key, &ret) < 0)
                die(_("could not retrieve `%s`"), config_display_key);
 
        if (!ret)
index 8af357a0a35d15267fb217337b3957be18577459..c49347c2d7b78221324bdf76b5760eaab6ecf2ad 100644 (file)
--- a/refspec.c
+++ b/refspec.c
@@ -245,6 +245,16 @@ int valid_fetch_refspec(const char *fetch_refspec_str)
        return ret;
 }
 
+int valid_remote_name(const char *name)
+{
+       int result;
+       struct strbuf refspec = STRBUF_INIT;
+       strbuf_addf(&refspec, "refs/heads/test:refs/remotes/%s/test", name);
+       result = valid_fetch_refspec(refspec.buf);
+       strbuf_release(&refspec);
+       return result;
+}
+
 void refspec_ref_prefixes(const struct refspec *rs,
                          struct strvec *ref_prefixes)
 {
index 95518323992366d0d64576f6492fd8c504bb7607..8b79891d3218d526ef1216bc1525b6040730185a 100644 (file)
--- a/refspec.h
+++ b/refspec.h
@@ -64,6 +64,7 @@ void refspec_appendn(struct refspec *rs, const char **refspecs, int nr);
 void refspec_clear(struct refspec *rs);
 
 int valid_fetch_refspec(const char *refspec);
+int valid_remote_name(const char *name);
 
 struct strvec;
 /*
index 32cc4a0c553b3e6f953be247bd593e07222dd5b5..0290b04891596bd4ebed0154c25b0713acd30d08 100644 (file)
@@ -44,7 +44,8 @@ struct options {
                from_promisor : 1,
 
                atomic : 1,
-               object_format : 1;
+               object_format : 1,
+               force_if_includes : 1;
        const struct git_hash_algo *hash_algo;
 };
 static struct options options;
@@ -131,6 +132,14 @@ static int set_option(const char *name, const char *value)
                string_list_append(&cas_options, val.buf);
                strbuf_release(&val);
                return 0;
+       } else if (!strcmp(name, TRANS_OPT_FORCE_IF_INCLUDES)) {
+               if (!strcmp(value, "true"))
+                       options.force_if_includes = 1;
+               else if (!strcmp(value, "false"))
+                       options.force_if_includes = 0;
+               else
+                       return -1;
+               return 0;
        } else if (!strcmp(name, "cloning")) {
                if (!strcmp(value, "true"))
                        options.cloning = 1;
@@ -1318,6 +1327,9 @@ static int push_git(struct discovery *heads, int nr_spec, const char **specs)
                strvec_push(&args, cas_option->string);
        strvec_push(&args, url.buf);
 
+       if (options.force_if_includes)
+               strvec_push(&args, "--force-if-includes");
+
        strvec_push(&args, "--stdin");
        for (i = 0; i < nr_spec; i++)
                packet_buf_write(&preamble, "%s\n", specs[i]);
index 8be67f0892b47d0f31f9f1579c80ad6420721a7b..8a6dbbb9030ad1464e4c3adf706ad54f1694408c 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1568,12 +1568,23 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
                 * with the remote-tracking branch to find the value
                 * to expect, but we did not have such a tracking
                 * branch.
+                *
+                * If the tip of the remote-tracking ref is unreachable
+                * from any reflog entry of its local ref indicating a
+                * possible update since checkout; reject the push.
                 */
                if (ref->expect_old_sha1) {
                        if (!oideq(&ref->old_oid, &ref->old_oid_expect))
                                reject_reason = REF_STATUS_REJECT_STALE;
+                       else if (ref->check_reachable && ref->unreachable)
+                               reject_reason =
+                                       REF_STATUS_REJECT_REMOTE_UPDATED;
                        else
-                               /* If the ref isn't stale then force the update. */
+                               /*
+                                * If the ref isn't stale, and is reachable
+                                * from from one of the reflog entries of
+                                * the local branch, force the update.
+                                */
                                force_ref_update = 1;
                }
 
@@ -2351,12 +2362,13 @@ int is_empty_cas(const struct push_cas_option *cas)
 
 /*
  * Look at remote.fetch refspec and see if we have a remote
- * tracking branch for the refname there.  Fill its current
- * value in sha1[].
+ * tracking branch for the refname there. Fill the name of
+ * the remote-tracking branch in *dst_refname, and the name
+ * of the commit object at its tip in oid[].
  * If we cannot do so, return negative to signal an error.
  */
 static int remote_tracking(struct remote *remote, const char *refname,
-                          struct object_id *oid)
+                          struct object_id *oid, char **dst_refname)
 {
        char *dst;
 
@@ -2365,9 +2377,150 @@ static int remote_tracking(struct remote *remote, const char *refname,
                return -1; /* no tracking ref for refname at remote */
        if (read_ref(dst, oid))
                return -1; /* we know what the tracking ref is but we cannot read it */
+
+       *dst_refname = dst;
        return 0;
 }
 
+/*
+ * The struct "reflog_commit_array" and related helper functions
+ * are used for collecting commits into an array during reflog
+ * traversals in "check_and_collect_until()".
+ */
+struct reflog_commit_array {
+       struct commit **item;
+       size_t nr, alloc;
+};
+
+#define REFLOG_COMMIT_ARRAY_INIT { NULL, 0, 0 }
+
+/* Append a commit to the array. */
+static void append_commit(struct reflog_commit_array *arr,
+                         struct commit *commit)
+{
+       ALLOC_GROW(arr->item, arr->nr + 1, arr->alloc);
+       arr->item[arr->nr++] = commit;
+}
+
+/* Free and reset the array. */
+static void free_commit_array(struct reflog_commit_array *arr)
+{
+       FREE_AND_NULL(arr->item);
+       arr->nr = arr->alloc = 0;
+}
+
+struct check_and_collect_until_cb_data {
+       struct commit *remote_commit;
+       struct reflog_commit_array *local_commits;
+       timestamp_t remote_reflog_timestamp;
+};
+
+/* Get the timestamp of the latest entry. */
+static int peek_reflog(struct object_id *o_oid, struct object_id *n_oid,
+                      const char *ident, timestamp_t timestamp,
+                      int tz, const char *message, void *cb_data)
+{
+       timestamp_t *ts = cb_data;
+       *ts = timestamp;
+       return 1;
+}
+
+static int check_and_collect_until(struct object_id *o_oid,
+                                  struct object_id *n_oid,
+                                  const char *ident, timestamp_t timestamp,
+                                  int tz, const char *message, void *cb_data)
+{
+       struct commit *commit;
+       struct check_and_collect_until_cb_data *cb = cb_data;
+
+       /* An entry was found. */
+       if (oideq(n_oid, &cb->remote_commit->object.oid))
+               return 1;
+
+       if ((commit = lookup_commit_reference(the_repository, n_oid)))
+               append_commit(cb->local_commits, commit);
+
+       /*
+        * If the reflog entry timestamp is older than the remote ref's
+        * latest reflog entry, there is no need to check or collect
+        * entries older than this one.
+        */
+       if (timestamp < cb->remote_reflog_timestamp)
+               return -1;
+
+       return 0;
+}
+
+#define MERGE_BASES_BATCH_SIZE 8
+
+/*
+ * Iterate through the reflog of the local ref to check if there is an entry
+ * for the given remote-tracking ref; runs until the timestamp of an entry is
+ * older than latest timestamp of remote-tracking ref's reflog. Any commits
+ * are that seen along the way are collected into an array to check if the
+ * remote-tracking ref is reachable from any of them.
+ */
+static int is_reachable_in_reflog(const char *local, const struct ref *remote)
+{
+       timestamp_t date;
+       struct commit *commit;
+       struct commit **chunk;
+       struct check_and_collect_until_cb_data cb;
+       struct reflog_commit_array arr = REFLOG_COMMIT_ARRAY_INIT;
+       size_t size = 0;
+       int ret = 0;
+
+       commit = lookup_commit_reference(the_repository, &remote->old_oid);
+       if (!commit)
+               goto cleanup_return;
+
+       /*
+        * Get the timestamp from the latest entry
+        * of the remote-tracking ref's reflog.
+        */
+       for_each_reflog_ent_reverse(remote->tracking_ref, peek_reflog, &date);
+
+       cb.remote_commit = commit;
+       cb.local_commits = &arr;
+       cb.remote_reflog_timestamp = date;
+       ret = for_each_reflog_ent_reverse(local, check_and_collect_until, &cb);
+
+       /* We found an entry in the reflog. */
+       if (ret > 0)
+               goto cleanup_return;
+
+       /*
+        * Check if the remote commit is reachable from any
+        * of the commits in the collected array, in batches.
+        */
+       for (chunk = arr.item; chunk < arr.item + arr.nr; chunk += size) {
+               size = arr.item + arr.nr - chunk;
+               if (MERGE_BASES_BATCH_SIZE < size)
+                       size = MERGE_BASES_BATCH_SIZE;
+
+               if ((ret = in_merge_bases_many(commit, size, chunk)))
+                       break;
+       }
+
+cleanup_return:
+       free_commit_array(&arr);
+       return ret;
+}
+
+/*
+ * Check for reachability of a remote-tracking
+ * ref in the reflog entries of its local ref.
+ */
+static void check_if_includes_upstream(struct ref *remote)
+{
+       struct ref *local = get_local_ref(remote->name);
+       if (!local)
+               return;
+
+       if (is_reachable_in_reflog(local->name, remote) <= 0)
+               remote->unreachable = 1;
+}
+
 static void apply_cas(struct push_cas_option *cas,
                      struct remote *remote,
                      struct ref *ref)
@@ -2382,8 +2535,12 @@ static void apply_cas(struct push_cas_option *cas,
                ref->expect_old_sha1 = 1;
                if (!entry->use_tracking)
                        oidcpy(&ref->old_oid_expect, &entry->expect);
-               else if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
+               else if (remote_tracking(remote, ref->name,
+                                        &ref->old_oid_expect,
+                                        &ref->tracking_ref))
                        oidclr(&ref->old_oid_expect);
+               else
+                       ref->check_reachable = cas->use_force_if_includes;
                return;
        }
 
@@ -2392,8 +2549,12 @@ static void apply_cas(struct push_cas_option *cas,
                return;
 
        ref->expect_old_sha1 = 1;
-       if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
+       if (remote_tracking(remote, ref->name,
+                           &ref->old_oid_expect,
+                           &ref->tracking_ref))
                oidclr(&ref->old_oid_expect);
+       else
+               ref->check_reachable = cas->use_force_if_includes;
 }
 
 void apply_push_cas(struct push_cas_option *cas,
@@ -2401,6 +2562,15 @@ void apply_push_cas(struct push_cas_option *cas,
                    struct ref *remote_refs)
 {
        struct ref *ref;
-       for (ref = remote_refs; ref; ref = ref->next)
+       for (ref = remote_refs; ref; ref = ref->next) {
                apply_cas(cas, remote, ref);
+
+               /*
+                * If "compare-and-swap" is in "use_tracking[_for_rest]"
+                * mode, and if "--force-if-includes" was specified, run
+                * the check.
+                */
+               if (ref->check_reachable)
+                       check_if_includes_upstream(ref);
+       }
 }
index d0e3f51574c11423ec8b8d17ca373eddc13ae753..3211abdf05ceef275309053af0dafc06f51fbb13 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -107,12 +107,20 @@ struct ref {
        struct object_id new_oid;
        struct object_id old_oid_expect; /* used by expect-old */
        char *symref;
+       char *tracking_ref;
        unsigned int
                force:1,
                forced_update:1,
                expect_old_sha1:1,
                exact_oid:1,
-               deletion:1;
+               deletion:1,
+               /* Need to check if local reflog reaches the remote tip. */
+               check_reachable:1,
+               /*
+                * Store the result of the check enabled by "check_reachable";
+                * implies the local reflog does not reach the remote tip.
+                */
+               unreachable:1;
 
        enum {
                REF_NOT_MATCHED = 0, /* initial value */
@@ -142,6 +150,7 @@ struct ref {
                REF_STATUS_REJECT_NEEDS_FORCE,
                REF_STATUS_REJECT_STALE,
                REF_STATUS_REJECT_SHALLOW,
+               REF_STATUS_REJECT_REMOTE_UPDATED,
                REF_STATUS_UPTODATE,
                REF_STATUS_REMOTE_REJECT,
                REF_STATUS_EXPECTING_REPORT,
@@ -348,6 +357,7 @@ struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map);
 
 struct push_cas_option {
        unsigned use_tracking_for_rest:1;
+       unsigned use_force_if_includes:1;
        struct push_cas {
                struct object_id expect;
                unsigned use_tracking:1;
index 88ccce2036b611f7ea1e6d8ac538f9ee4e155395..f7fff0f5ab837e05cd369ed7630e8e2648e9aba8 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "config.h"
 #include "repository.h"
+#include "midx.h"
 
 #define UPDATE_DEFAULT_BOOL(s,v) do { if (s == -1) { s = v; } } while(0)
 
@@ -52,6 +53,11 @@ void prepare_repo_settings(struct repository *r)
                r->settings.pack_use_sparse = value;
        UPDATE_DEFAULT_BOOL(r->settings.pack_use_sparse, 1);
 
+       value = git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0);
+       if (value || !repo_config_get_bool(r, "core.multipackindex", &value))
+               r->settings.core_multi_pack_index = value;
+       UPDATE_DEFAULT_BOOL(r->settings.core_multi_pack_index, 1);
+
        if (!repo_config_get_bool(r, "feature.manyfiles", &value) && value) {
                UPDATE_DEFAULT_BOOL(r->settings.index_version, 4);
                UPDATE_DEFAULT_BOOL(r->settings.core_untracked_cache, UNTRACKED_CACHE_WRITE);
index bacf843d460d2ff8a28511288055dc12dfbb53c6..b385ca3c94b62b2095b0ddb1b2b7adcce037da9a 100644 (file)
@@ -39,6 +39,8 @@ struct repo_settings {
 
        int pack_use_sparse;
        enum fetch_negotiation_setting fetch_negotiation_algorithm;
+
+       int core_multi_pack_index;
 };
 
 struct repository {
index c9698070fca18e403add1576ae9e1d0e818e16e7..eb4a44270bad2b2576bc93cb282b5f5bdf4dee83 100644 (file)
@@ -299,6 +299,7 @@ static int check_to_send_update(const struct ref *ref, const struct send_pack_ar
        case REF_STATUS_REJECT_FETCH_FIRST:
        case REF_STATUS_REJECT_NEEDS_FORCE:
        case REF_STATUS_REJECT_STALE:
+       case REF_STATUS_REJECT_REMOTE_UPDATED:
        case REF_STATUS_REJECT_NODELETE:
                return CHECK_REF_STATUS_REJECTED;
        case REF_STATUS_UPTODATE:
index d76cbded00d0d39f9357861066a9670c8ed046ac..12443965c7befa8063cf5a7a52e00c380ff50f83 100644 (file)
@@ -314,8 +314,6 @@ int sequencer_remove_state(struct replay_opts *opts)
                }
        }
 
-       free(opts->committer_name);
-       free(opts->committer_email);
        free(opts->gpg_sign);
        free(opts->strategy);
        for (i = 0; i < opts->xopts_nr; i++)
@@ -1460,8 +1458,8 @@ static int try_to_commit(struct repository *r,
                } else {
                        reset_ident_date();
                }
-               committer = fmt_ident(opts->committer_name,
-                                     opts->committer_email,
+               committer = fmt_ident(getenv("GIT_COMMITTER_NAME"),
+                                     getenv("GIT_COMMITTER_EMAIL"),
                                      WANT_COMMITTER_IDENT,
                                      opts->ignore_date ? NULL : date.buf,
                                      IDENT_STRICT);
@@ -3677,7 +3675,9 @@ static int do_merge(struct repository *r,
                strvec_push(&cmd.args, "-F");
                strvec_push(&cmd.args, git_path_merge_msg(r));
                if (opts->gpg_sign)
-                       strvec_push(&cmd.args, opts->gpg_sign);
+                       strvec_pushf(&cmd.args, "-S%s", opts->gpg_sign);
+               else
+                       strvec_push(&cmd.args, "--no-gpg-sign");
 
                /* Add the tips to be merged */
                for (j = to_merge; j; j = j->next)
@@ -3689,7 +3689,6 @@ static int do_merge(struct repository *r,
                                NULL, 0);
                rollback_lock_file(&lock);
 
-               rollback_lock_file(&lock);
                ret = run_command(&cmd);
 
                /* force re-reading of the cache */
@@ -4467,22 +4466,6 @@ static int commit_staged_changes(struct repository *r,
        return 0;
 }
 
-static int init_committer(struct replay_opts *opts)
-{
-       struct ident_split id;
-       const char *committer;
-
-       committer = git_committer_info(IDENT_STRICT);
-       if (split_ident_line(&id, committer, strlen(committer)) < 0)
-               return error(_("invalid committer '%s'"), committer);
-       opts->committer_name =
-               xmemdupz(id.name_begin, id.name_end - id.name_begin);
-       opts->committer_email =
-               xmemdupz(id.mail_begin, id.mail_end - id.mail_begin);
-
-       return 0;
-}
-
 int sequencer_continue(struct repository *r, struct replay_opts *opts)
 {
        struct todo_list todo_list = TODO_LIST_INIT;
@@ -4494,9 +4477,6 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
        if (read_populate_opts(opts))
                return -1;
        if (is_rebase_i(opts)) {
-               if (opts->committer_date_is_author_date && init_committer(opts))
-                       return -1;
-
                if ((res = read_populate_todo(r, &todo_list, opts)))
                        goto release_todo_list;
 
@@ -5391,9 +5371,6 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
 
        res = -1;
 
-       if (opts->committer_date_is_author_date && init_committer(opts))
-               goto cleanup;
-
        if (checkout_onto(r, opts, onto_name, &oid, orig_head))
                goto cleanup;
 
index b2a501e445316fc5a6d5befb341be3cfc1c74077..f925e349c52d127777a27c56ba463f335649e6fb 100644 (file)
@@ -50,8 +50,6 @@ struct replay_opts {
 
        int mainline;
 
-       char *committer_name;
-       char *committer_email;
        char *gpg_sign;
        enum commit_msg_cleanup_mode default_msg_cleanup;
        int explicit_cleanup;
index 0a60662fa64b34a9b70c9c362fac02aa8c61083c..6f9e02673201ac111acd7f69e0388282de834ed5 100644 (file)
@@ -3,6 +3,7 @@
 #include "config.h"
 #include "sideband.h"
 #include "help.h"
+#include "pkt-line.h"
 
 struct keyword_entry {
        /*
@@ -114,7 +115,8 @@ static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
 #define ANSI_SUFFIX "\033[K"
 #define DUMB_SUFFIX "        "
 
-int demultiplex_sideband(const char *me, char *buf, int len,
+int demultiplex_sideband(const char *me, int status,
+                        char *buf, int len,
                         int die_on_error,
                         struct strbuf *scratch,
                         enum sideband_type *sideband_type)
@@ -130,17 +132,30 @@ int demultiplex_sideband(const char *me, char *buf, int len,
                        suffix = DUMB_SUFFIX;
        }
 
-       if (len == 0) {
-               *sideband_type = SIDEBAND_FLUSH;
-               goto cleanup;
-       }
-       if (len < 1) {
+       if (status == PACKET_READ_EOF) {
                strbuf_addf(scratch,
-                           "%s%s: protocol error: no band designator",
+                           "%s%s: unexpected disconnect while reading sideband packet",
                            scratch->len ? "\n" : "", me);
                *sideband_type = SIDEBAND_PROTOCOL_ERROR;
                goto cleanup;
        }
+
+       if (len < 0)
+               BUG("negative length on non-eof packet read");
+
+       if (len == 0) {
+               if (status == PACKET_READ_NORMAL) {
+                       strbuf_addf(scratch,
+                                   "%s%s: protocol error: missing sideband designator",
+                                   scratch->len ? "\n" : "", me);
+                       *sideband_type = SIDEBAND_PROTOCOL_ERROR;
+               } else {
+                       /* covers flush, delim, etc */
+                       *sideband_type = SIDEBAND_FLUSH;
+               }
+               goto cleanup;
+       }
+
        band = buf[0] & 0xff;
        buf[len] = '\0';
        len--;
@@ -190,7 +205,7 @@ int demultiplex_sideband(const char *me, char *buf, int len,
                return 0;
        case 1:
                *sideband_type = SIDEBAND_PRIMARY;
-               break;
+               return 1;
        default:
                strbuf_addf(scratch, "%s%s: protocol error: bad band #%d",
                            scratch->len ? "\n" : "", me, band);
index 227740a58e58bd25c4625bc1963106542bd99779..5a25331be55d30dbc6b4d777da3cf015118dcf3d 100644 (file)
@@ -18,8 +18,12 @@ enum sideband_type {
  *
  * scratch must be a struct strbuf allocated by the caller. It is used to store
  * progress messages split across multiple packets.
+ *
+ * The "status" parameter is a pkt-line response as returned by
+ * packet_read_with_status() (e.g., PACKET_READ_NORMAL).
  */
-int demultiplex_sideband(const char *me, char *buf, int len,
+int demultiplex_sideband(const char *me, int status,
+                        char *buf, int len,
                         int die_on_error,
                         struct strbuf *scratch,
                         enum sideband_type *sideband_type);
index c83fd18861f31039bc14ec71a9a897b03359b2c7..882d26eee30b115f4c656e0ab5048f4e2e96319b 100644 (file)
@@ -34,6 +34,7 @@ CHAINLINTTMP_SQ = $(subst ','\'',$(CHAINLINTTMP))
 T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
 TGITWEB = $(sort $(wildcard t95[0-9][0-9]-*.sh))
 THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh)))
+TPERF = $(sort $(wildcard perf/p[0-9][0-9][0-9][0-9]-*.sh))
 CHAINLINTTESTS = $(sort $(patsubst chainlint/%.test,%,$(wildcard chainlint/*.test)))
 CHAINLINT = sed -f chainlint.sed
 
@@ -81,17 +82,17 @@ test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax \
        test-lint-filenames
 
 test-lint-duplicates:
-       @dups=`echo $(T) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
+       @dups=`echo $(T) $(TPERF) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
                test -z "$$dups" || { \
                echo >&2 "duplicate test numbers:" $$dups; exit 1; }
 
 test-lint-executable:
-       @bad=`for i in $(T); do test -x "$$i" || echo $$i; done` && \
+       @bad=`for i in $(T) $(TPERF); do test -x "$$i" || echo $$i; done` && \
                test -z "$$bad" || { \
                echo >&2 "non-executable tests:" $$bad; exit 1; }
 
 test-lint-shell-syntax:
-       @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T) $(THELPERS)
+       @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T) $(THELPERS) $(TPERF)
 
 test-lint-filenames:
        @# We do *not* pass a glob to ls-files but use grep instead, to catch
index 2adaf7c2d2747a1426b11a8d75e0cac667232b60..c730a7077057d4104a8d4f18737309a2e2dcbdf4 100644 (file)
--- a/t/README
+++ b/t/README
@@ -258,16 +258,21 @@ For an individual test suite --run could be used to specify that
 only some tests should be run or that some tests should be
 excluded from a run.
 
-The argument for --run is a list of individual test numbers or
-ranges with an optional negation prefix that define what tests in
-a test suite to include in the run.  A range is two numbers
-separated with a dash and matches a range of tests with both ends
-been included.  You may omit the first or the second number to
-mean "from the first test" or "up to the very last test"
-respectively.
-
-Optional prefix of '!' means that the test or a range of tests
-should be excluded from the run.
+The argument for --run, <test-selector>, is a list of description
+substrings or globs or individual test numbers or ranges with an
+optional negation prefix (of '!') that define what tests in a test
+suite to include (or exclude, if negated) in the run.  A range is two
+numbers separated with a dash and matches a range of tests with both
+ends been included.  You may omit the first or the second number to
+mean "from the first test" or "up to the very last test" respectively.
+
+The argument to --run is split on commas into separate strings,
+numbers, and ranges, and picks all tests that match any of the
+individual selection criteria.  If the substring of the description
+text that you want to match includes a comma, use the glob character
+'?' instead.  For example --run='rebase,merge?cherry-pick' would match
+on all tests that match either the glob *rebase* or the glob
+*merge?cherry-pick*.
 
 If --run starts with an unprefixed number or range the initial
 set of tests to run is empty. If the first item starts with '!'
@@ -275,9 +280,6 @@ all the tests are added to the initial set.  After initial set is
 determined every test number or range is added or excluded from
 the set one by one, from left to right.
 
-Individual numbers or ranges could be separated either by a space
-or a comma.
-
 For example, to run only tests up to a specific test (21), one
 could do this:
 
@@ -290,7 +292,7 @@ or this:
 Common case is to run several setup tests (1, 2, 3) and then a
 specific test (21) that relies on that setup:
 
-    $ sh ./t9200-git-cvsexport-commit.sh --run='1 2 3 21'
+    $ sh ./t9200-git-cvsexport-commit.sh --run='1,2,3,21'
 
 or:
 
@@ -298,17 +300,17 @@ or:
 
 or:
 
-    $ sh ./t9200-git-cvsexport-commit.sh --run='-3 21'
+    $ sh ./t9200-git-cvsexport-commit.sh --run='-3,21'
 
 As noted above, the test set is built by going through the items
 from left to right, so this:
 
-    $ sh ./t9200-git-cvsexport-commit.sh --run='1-4 !3'
+    $ sh ./t9200-git-cvsexport-commit.sh --run='1-4,!3'
 
 will run tests 1, 2, and 4.  Items that come later have higher
 precedence.  It means that this:
 
-    $ sh ./t9200-git-cvsexport-commit.sh --run='!3 1-4'
+    $ sh ./t9200-git-cvsexport-commit.sh --run='!3,1-4'
 
 would just run tests from 1 to 4, including 3.
 
@@ -317,6 +319,18 @@ test in the test suite except from 7 up to 11:
 
     $ sh ./t9200-git-cvsexport-commit.sh --run='!7-11'
 
+Sometimes there may be multiple tests with e.g. "setup" in their name
+that are needed and rather than figuring out the number for all of them
+we can just use "setup" as a substring/glob to match against the test
+description:
+
+    $ sh ./t0050-filesystem.sh --run=setup,9-11
+
+or one could select both the setup tests and the rename ones (assuming all
+relevant tests had those words in their descriptions):
+
+    $ sh ./t0050-filesystem.sh --run=setup,rename
+
 Some tests in a test suite rely on the previous tests performing
 certain actions, specifically some tests are designated as
 "setup" test, so you cannot _arbitrarily_ disable one test and
index 69152958e58eafea73264b1edb99e729613c6843..5e638f0b970d37e57cc0b17d1aa9e17b29e52a0e 100644 (file)
@@ -84,6 +84,25 @@ static void unpack_sideband(void)
        }
 }
 
+static int send_split_sideband(void)
+{
+       const char *part1 = "Hello,";
+       const char *primary = "\001primary: regular output\n";
+       const char *part2 = " world!\n";
+
+       send_sideband(1, 2, part1, strlen(part1), LARGE_PACKET_MAX);
+       packet_write(1, primary, strlen(primary));
+       send_sideband(1, 2, part2, strlen(part2), LARGE_PACKET_MAX);
+       packet_response_end(1);
+
+       return 0;
+}
+
+static int receive_sideband(void)
+{
+       return recv_sideband("sideband", 0, 1);
+}
+
 int cmd__pkt_line(int argc, const char **argv)
 {
        if (argc < 2)
@@ -95,6 +114,10 @@ int cmd__pkt_line(int argc, const char **argv)
                unpack();
        else if (!strcmp(argv[1], "unpack-sideband"))
                unpack_sideband();
+       else if (!strcmp(argv[1], "send-split-sideband"))
+               send_split_sideband();
+       else if (!strcmp(argv[1], "receive-sideband"))
+               receive_sideband();
        else
                die("invalid argument '%s'", argv[1]);
 
index 87a759149faf37c748568c31b0a9017ae5ec6940..bd3fa3c6da40d7018baf56b3360ed68f046eca48 100644 (file)
@@ -144,7 +144,7 @@ create_lib_submodule_repo () {
                git checkout -b valid_sub1 &&
                git revert HEAD &&
 
-               git checkout master
+               git checkout "${GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME-master}"
        )
 }
 
index 8c47155a7c86eebe4b8189601866b2b4df4a4f22..fcb0e8865e49335d5a9afe3941bcea2c58f4bcd8 100644 (file)
@@ -1,7 +1,7 @@
 -include ../../config.mak
 export GIT_TEST_OPTIONS
 
-all: perf
+all: test-lint perf
 
 perf: pre-clean
        ./run
@@ -12,4 +12,7 @@ pre-clean:
 clean:
        rm -rf build "trash directory".* test-results
 
+test-lint:
+       $(MAKE) -C .. test-lint
+
 .PHONY: all perf pre-clean clean
index bd649afa978393373c209b507f3713c8f716986c..fb9127a66f7561e528584c326a7d2539d2647dcb 100644 (file)
@@ -28,6 +28,8 @@ the tests on the current git repository.
     7810.3: grep --cached, cheap regex       3.07(3.02+0.25)
     7810.4: grep --cached, expensive regex   9.39(30.57+0.24)
 
+Output format is in seconds "Elapsed(User + System)"
+
 You can compare multiple repositories and even git revisions with the
 'run' script:
 
index d202aaed06fc6cba62a1955e790bbe324d365235..7a0bb294480a6d98ec3db506dff3eafa57cb30f0 100755 (executable)
@@ -9,16 +9,16 @@ test_expect_success 'setup rebasing on top of a lot of changes' '
        git checkout -f -B base &&
        git checkout -B to-rebase &&
        git checkout -B upstream &&
-       for i in $(seq 100)
+       for i in $(test_seq 100)
        do
                # simulate huge diffs
                echo change$i >unrelated-file$i &&
-               seq 1000 >>unrelated-file$i &&
+               test_seq 1000 >>unrelated-file$i &&
                git add unrelated-file$i &&
                test_tick &&
                git commit -m commit$i unrelated-file$i &&
                echo change$i >unrelated-file$i &&
-               seq 1000 | tac >>unrelated-file$i &&
+               test_seq 1000 | tac >>unrelated-file$i &&
                git add unrelated-file$i &&
                test_tick &&
                git commit -m commit$i-reverse unrelated-file$i ||
index def7ecdbc786c6472e5b7909a97650a1034cecb2..fb20fe0937f3578cae829d9b7d4dfdb68bb304b0 100755 (executable)
@@ -114,63 +114,77 @@ test_expect_success "setup for fsmonitor" '
        fi &&
 
        git config core.fsmonitor "$INTEGRATION_SCRIPT" &&
-       git update-index --fsmonitor
+       git update-index --fsmonitor &&
+       mkdir 1_file 10_files 100_files 1000_files 10000_files &&
+       for i in $(test_seq 1 10); do touch 10_files/$i; done &&
+       for i in $(test_seq 1 100); do touch 100_files/$i; done &&
+       for i in $(test_seq 1 1000); do touch 1000_files/$i; done &&
+       for i in $(test_seq 1 10000); do touch 10000_files/$i; done &&
+       git add 1_file 10_files 100_files 1000_files 10000_files &&
+       git commit -m "Add files" &&
+       git status  # Warm caches
 '
 
-if test -n "$GIT_PERF_7519_DROP_CACHE"; then
-       test-tool drop-caches
-fi
+test_perf_w_drop_caches () {
+       if test -n "$GIT_PERF_7519_DROP_CACHE"; then
+               test-tool drop-caches
+       fi
 
-test_perf "status (fsmonitor=$INTEGRATION_SCRIPT)" '
-       git status
-'
+       test_perf "$@"
+}
 
-if test -n "$GIT_PERF_7519_DROP_CACHE"; then
-       test-tool drop-caches
-fi
+test_fsmonitor_suite() {
+       test_perf_w_drop_caches "status (fsmonitor=$INTEGRATION_SCRIPT)" '
+               git status
+       '
 
-test_perf "status -uno (fsmonitor=$INTEGRATION_SCRIPT)" '
-       git status -uno
-'
+       test_perf_w_drop_caches "status -uno (fsmonitor=$INTEGRATION_SCRIPT)" '
+               git status -uno
+       '
 
-if test -n "$GIT_PERF_7519_DROP_CACHE"; then
-       test-tool drop-caches
-fi
+       test_perf_w_drop_caches "status -uall (fsmonitor=$INTEGRATION_SCRIPT)" '
+               git status -uall
+       '
 
-test_perf "status -uall (fsmonitor=$INTEGRATION_SCRIPT)" '
-       git status -uall
-'
+       test_perf_w_drop_caches "diff (fsmonitor=$INTEGRATION_SCRIPT)" '
+               git diff
+       '
 
-test_expect_success "setup without fsmonitor" '
-       unset INTEGRATION_SCRIPT &&
-       git config --unset core.fsmonitor &&
-       git update-index --no-fsmonitor
-'
+       test_perf_w_drop_caches "diff -- 0_files (fsmonitor=$INTEGRATION_SCRIPT)" '
+               git diff -- 1_file
+       '
 
-if test -n "$GIT_PERF_7519_DROP_CACHE"; then
-       test-tool drop-caches
-fi
+       test_perf_w_drop_caches "diff -- 10_files (fsmonitor=$INTEGRATION_SCRIPT)" '
+               git diff -- 10_files
+       '
 
-test_perf "status (fsmonitor=$INTEGRATION_SCRIPT)" '
-       git status
-'
+       test_perf_w_drop_caches "diff -- 100_files (fsmonitor=$INTEGRATION_SCRIPT)" '
+               git diff -- 100_files
+       '
 
-if test -n "$GIT_PERF_7519_DROP_CACHE"; then
-       test-tool drop-caches
-fi
+       test_perf_w_drop_caches "diff -- 1000_files (fsmonitor=$INTEGRATION_SCRIPT)" '
+               git diff -- 1000_files
+       '
 
-test_perf "status -uno (fsmonitor=$INTEGRATION_SCRIPT)" '
-       git status -uno
-'
+       test_perf_w_drop_caches "diff -- 10000_files (fsmonitor=$INTEGRATION_SCRIPT)" '
+               git diff -- 10000_files
+       '
 
-if test -n "$GIT_PERF_7519_DROP_CACHE"; then
-       test-tool drop-caches
-fi
+       test_perf_w_drop_caches "add (fsmonitor=$INTEGRATION_SCRIPT)" '
+               git add  --all
+       '
+}
+
+test_fsmonitor_suite
 
-test_perf "status -uall (fsmonitor=$INTEGRATION_SCRIPT)" '
-       git status -uall
+test_expect_success "setup without fsmonitor" '
+       unset INTEGRATION_SCRIPT &&
+       git config --unset core.fsmonitor &&
+       git update-index --no-fsmonitor
 '
 
+test_fsmonitor_suite
+
 if test_have_prereq WATCHMAN
 then
        watchman watch-del "$GIT_WORK_TREE" >/dev/null 2>&1 &&
index 923281af93981d5b6f67ace54f41849b44ecf0b1..22489c24dc4b37a5057b6087f887b638cd2a090b 100755 (executable)
@@ -430,7 +430,7 @@ test_expect_success 'GIT_SKIP_TESTS does not skip unmatched suite' "
 
 test_expect_success '--run basic' "
        run_sub_test_lib_test run-basic \
-               '--run basic' --run='1 3 5' <<-\\EOF &&
+               '--run basic' --run='1,3,5' <<-\\EOF &&
        for i in 1 2 3 4 5 6
        do
                test_expect_success \"passing test #\$i\" 'true'
@@ -472,7 +472,7 @@ test_expect_success '--run with a range' "
 
 test_expect_success '--run with two ranges' "
        run_sub_test_lib_test run-two-ranges \
-               '--run with two ranges' --run='1-2 5-6' <<-\\EOF &&
+               '--run with two ranges' --run='1-2,5-6' <<-\\EOF &&
        for i in 1 2 3 4 5 6
        do
                test_expect_success \"passing test #\$i\" 'true'
@@ -556,7 +556,7 @@ test_expect_success '--run with basic negation' "
 
 test_expect_success '--run with two negations' "
        run_sub_test_lib_test run-two-neg \
-               '--run with two negations' --run='"'!3 !6'"' <<-\\EOF &&
+               '--run with two negations' --run='"'!3,!6'"' <<-\\EOF &&
        for i in 1 2 3 4 5 6
        do
                test_expect_success \"passing test #\$i\" 'true'
@@ -577,7 +577,7 @@ test_expect_success '--run with two negations' "
 
 test_expect_success '--run a range and negation' "
        run_sub_test_lib_test run-range-and-neg \
-               '--run a range and negation' --run='"'-4 !2'"' <<-\\EOF &&
+               '--run a range and negation' --run='"'-4,!2'"' <<-\\EOF &&
        for i in 1 2 3 4 5 6
        do
                test_expect_success \"passing test #\$i\" 'true'
@@ -620,7 +620,7 @@ test_expect_success '--run range negation' "
 test_expect_success '--run include, exclude and include' "
        run_sub_test_lib_test run-inc-neg-inc \
                '--run include, exclude and include' \
-               --run='"'1-5 !1-3 2'"' <<-\\EOF &&
+               --run='"'1-5,!1-3,2'"' <<-\\EOF &&
        for i in 1 2 3 4 5 6
        do
                test_expect_success \"passing test #\$i\" 'true'
@@ -664,7 +664,7 @@ test_expect_success '--run include, exclude and include, comma separated' "
 test_expect_success '--run exclude and include' "
        run_sub_test_lib_test run-neg-inc \
                '--run exclude and include' \
-               --run='"'!3- 5'"' <<-\\EOF &&
+               --run='"'!3-,5'"' <<-\\EOF &&
        for i in 1 2 3 4 5 6
        do
                test_expect_success \"passing test #\$i\" 'true'
@@ -705,7 +705,31 @@ test_expect_success '--run empty selectors' "
        EOF
 "
 
-test_expect_success '--run invalid range start' "
+test_expect_success '--run substring selector' "
+       run_sub_test_lib_test run-substring-selector \
+               '--run empty selectors' \
+               --run='relevant' <<-\\EOF &&
+       test_expect_success \"relevant test\" 'true'
+       for i in 1 2 3 4 5 6
+       do
+               test_expect_success \"other test #\$i\" 'true'
+       done
+       test_done
+       EOF
+       check_sub_test_lib_test run-substring-selector <<-\\EOF
+       > ok 1 - relevant test
+       > ok 2 # skip other test #1 (--run)
+       > ok 3 # skip other test #2 (--run)
+       > ok 4 # skip other test #3 (--run)
+       > ok 5 # skip other test #4 (--run)
+       > ok 6 # skip other test #5 (--run)
+       > ok 7 # skip other test #6 (--run)
+       > # passed all 7 test(s)
+       > 1..7
+       EOF
+"
+
+test_expect_success '--run keyword selection' "
        run_sub_test_lib_test_err run-inv-range-start \
                '--run invalid range start' \
                --run='a-5' <<-\\EOF &&
@@ -735,21 +759,6 @@ test_expect_success '--run invalid range end' "
        EOF_ERR
 "
 
-test_expect_success '--run invalid selector' "
-       run_sub_test_lib_test_err run-inv-selector \
-               '--run invalid selector' \
-               --run='1?' <<-\\EOF &&
-       test_expect_success \"passing test #1\" 'true'
-       test_done
-       EOF
-       check_sub_test_lib_test_err run-inv-selector \
-               <<-\\EOF_OUT 3<<-\\EOF_ERR
-       > FATAL: Unexpected exit with code 1
-       EOF_OUT
-       > error: --run: invalid non-numeric in test selector: '1?'
-       EOF_ERR
-"
-
 
 test_set_prereq HAVEIT
 haveit=no
@@ -1191,7 +1200,7 @@ test_expect_success 'writing this tree with --missing-ok' '
 test_expect_success 'git read-tree followed by write-tree should be idempotent' '
        rm -f .git/index &&
        git read-tree $tree &&
-       test -f .git/index &&
+       test_path_is_file .git/index &&
        newtree=$(git write-tree) &&
        test "$newtree" = "$tree"
 '
index 2f7c3dcd0f85d41409a0a40520d2af821b2c147d..69a320489fca1f0df400e289cde6c1d250efe066 100755 (executable)
@@ -553,14 +553,21 @@ test_expect_success '--initial-branch' '
 
 test_expect_success 'overridden default initial branch name (config)' '
        test_config_global init.defaultBranch nmb &&
-       git init initial-branch-config &&
+       GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= git init initial-branch-config &&
        git -C initial-branch-config symbolic-ref HEAD >actual &&
        grep nmb actual
 '
 
+test_expect_success 'overridden default main branch name (env)' '
+       test_config_global init.defaultBranch nmb &&
+       GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=env git init main-branch-env &&
+       git -C main-branch-env symbolic-ref HEAD >actual &&
+       grep env actual
+'
+
 test_expect_success 'invalid default branch name' '
-       test_config_global init.defaultBranch "with space" &&
-       test_must_fail git init initial-branch-invalid 2>err &&
+       test_must_fail env GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME="with space" \
+               git init initial-branch-invalid 2>err &&
        test_i18ngrep "invalid branch name" err
 '
 
index 7b111a56fdd3e13d34b538e52c2659a8f2e188a2..8d59905ef099bf0ab46d0681568dd17d7ed85d98 100755 (executable)
@@ -34,4 +34,22 @@ test_expect_success 'check for a bug in the regex routines' '
        test-tool regex --bug
 '
 
+test_expect_success 'incomplete sideband messages are reassembled' '
+       test-tool pkt-line send-split-sideband >split-sideband &&
+       test-tool pkt-line receive-sideband <split-sideband 2>err &&
+       grep "Hello, world" err
+'
+
+test_expect_success 'eof on sideband message is reported' '
+       printf 1234 >input &&
+       test-tool pkt-line receive-sideband <input 2>err &&
+       test_i18ngrep "unexpected disconnect" err
+'
+
+test_expect_success 'missing sideband designator is reported' '
+       printf 0004 >input &&
+       test-tool pkt-line receive-sideband <input 2>err &&
+       test_i18ngrep "missing sideband" err
+'
+
 test_done
index bc2d74098f035cde80de8282736188c6f6db3066..a18f8a473bfc667023c433a6c55c05defcc2e9f0 100755 (executable)
@@ -265,6 +265,32 @@ test_expect_success 'internal getpass does not ask for known username' '
        EOF
 '
 
+test_expect_success 'git-credential respects core.askPass' '
+       write_script alternate-askpass <<-\EOF &&
+       echo >&2 "alternate askpass invoked"
+       echo alternate-value
+       EOF
+       test_config core.askpass "$PWD/alternate-askpass" &&
+       (
+               # unset GIT_ASKPASS set by lib-credential.sh which would
+               # override our config, but do so in a subshell so that we do
+               # not interfere with other tests
+               sane_unset GIT_ASKPASS &&
+               check fill <<-\EOF
+               protocol=http
+               host=example.com
+               --
+               protocol=http
+               host=example.com
+               username=alternate-value
+               password=alternate-value
+               --
+               alternate askpass invoked
+               alternate askpass invoked
+               EOF
+       )
+'
+
 HELPER="!f() {
                cat >/dev/null
                echo username=foo
index 770e7be363cdaa0fbbfc52a03cbc195c0920aad1..4c01e08551e5b8ea203f53b1e50f81a36ce27731 100755 (executable)
@@ -585,10 +585,10 @@ test_expect_success 'stdin fails on unbalanced quotes' '
        grep "fatal: badly quoted argument: \\\"master" err
 '
 
-test_expect_success 'stdin fails on invalid escape' '
-       echo "create $a \"ma\zter\"" >stdin &&
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'stdin fails on invalid escape' '
+       echo "create $a \"ma\zn\"" >stdin &&
        test_must_fail git update-ref --stdin <stdin 2>err &&
-       grep "fatal: badly quoted argument: \\\"ma\\\\zter\\\"" err
+       grep "fatal: badly quoted argument: \\\"ma\\\\zn\\\"" err
 '
 
 test_expect_success 'stdin fails on junk after quoted argument' '
@@ -704,9 +704,9 @@ test_expect_success 'stdin succeeds with quoted argument' '
        test_cmp expect actual
 '
 
-test_expect_success 'stdin succeeds with escaped character' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'stdin succeeds with escaped character' '
        git update-ref -d $a &&
-       echo "create $a \"ma\\163ter\"" >stdin &&
+       echo "create $a \"ma\\151n\"" >stdin &&
        git update-ref --stdin <stdin &&
        git rev-parse $m >expect &&
        git rev-parse $a >actual &&
index a12afe93f32948dd994d55418ed93485757d2ba8..a9352b08a8b80e4a58131f31f98eb7481914ecbe 100755 (executable)
@@ -88,9 +88,17 @@ test_expect_success 'checkout all stage 2 to temporary files' '
        done
 '
 
+test_expect_success 'checkout all stages of unknown path' '
+       rm -f path* .merge_* actual &&
+       test_must_fail git checkout-index --stage=all --temp \
+               -- does-not-exist 2>stderr &&
+       test_i18ngrep not.in.the.cache stderr
+'
+
 test_expect_success 'checkout all stages/one file to nothing' '
        rm -f path* .merge_* actual &&
-       git checkout-index --stage=all --temp -- path0 >actual &&
+       git checkout-index --stage=all --temp -- path0 >actual 2>stderr &&
+       test_must_be_empty stderr &&
        test_line_count = 0 actual
 '
 
index 57cbdfe9bce93ddd33df08fec506be93e362db58..8e181dbf01c46a324b54471cc62e1c9291852079 100755 (executable)
@@ -21,4 +21,15 @@ test_expect_success 'checkout-index -h in broken repository' '
        test_i18ngrep "[Uu]sage" broken/usage
 '
 
+test_expect_success 'checkout-index reports errors (cmdline)' '
+       test_must_fail git checkout-index -- does-not-exist 2>stderr &&
+       test_i18ngrep not.in.the.cache stderr
+'
+
+test_expect_success 'checkout-index reports errors (stdin)' '
+       echo does-not-exist |
+       test_must_fail git checkout-index --stdin 2>stderr &&
+       test_i18ngrep not.in.the.cache stderr
+'
+
 test_done
index 47aeb0b1674c01fd35e3aef759756348026a04bb..d91a329eb31af78110ced886c2680346f94236fc 100755 (executable)
@@ -18,6 +18,10 @@ test_expect_success PERL 'setup' '
 
 # note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
 
+# NEEDSWORK: Since the builtin add-p is used when $GIT_TEST_ADD_I_USE_BUILTIN
+# is given, we should replace the PERL prerequisite with an ADD_I prerequisite
+# which first checks if $GIT_TEST_ADD_I_USE_BUILTIN is defined before checking
+# PERL.
 test_expect_success PERL 'saying "n" does nothing' '
        set_and_save_state dir/foo work head &&
        test_write_lines n n | git checkout -p &&
@@ -59,6 +63,13 @@ test_expect_success PERL 'git checkout -p HEAD with change already staged' '
        verify_state dir/foo head head
 '
 
+test_expect_success PERL 'git checkout -p HEAD^...' '
+       # the third n is to get out in case it mistakenly does not apply
+       test_write_lines n y n | git checkout -p HEAD^... &&
+       verify_saved_state bar &&
+       verify_state dir/foo parent parent
+'
+
 test_expect_success PERL 'git checkout -p HEAD^' '
        # the third n is to get out in case it mistakenly does not apply
        test_write_lines n y n | git checkout -p HEAD^ &&
index accfa9aa4bd82a5f13d3ce5e3755f33f47b38e3e..a4f8d3a67e8a4a36d6bccdda6a83da15c53ee5f3 100755 (executable)
@@ -166,6 +166,17 @@ test_expect_success '--no-guess suppresses branch auto-vivification' '
        test_branch master
 '
 
+test_expect_success 'checkout.guess = false suppresses branch auto-vivification' '
+       git checkout -B master &&
+       status_uno_is_clean &&
+       test_might_fail git branch -D bar &&
+
+       test_config checkout.guess false &&
+       test_must_fail git checkout bar &&
+       test_must_fail git rev-parse --verify refs/heads/bar &&
+       test_branch master
+'
+
 test_expect_success 'setup more remotes with unconventional refspecs' '
        git checkout -B master &&
        status_uno_is_clean &&
index 2c1b8c0d6d22469fadd9eeb05a19c1b7a53d0f51..68c9101b02f32d943ec5c167d7a726747f5970e4 100755 (executable)
@@ -85,9 +85,12 @@ test_expect_success 'switching ignores file of same branch name' '
        test_cmp expected actual
 '
 
-test_expect_success 'guess and create branch ' '
+test_expect_success 'guess and create branch' '
        test_when_finished git switch master &&
        test_must_fail git switch --no-guess foo &&
+       test_config checkout.guess false &&
+       test_must_fail git switch foo &&
+       test_config checkout.guess true &&
        git switch foo &&
        echo refs/heads/foo >expected &&
        git symbolic-ref HEAD >actual &&
index 98b2476e7c79f74745d8276035f94fe52a7133e5..b5c5c0ff7e37ced9b6b50a06e4ec3e30c536cd53 100755 (executable)
@@ -60,6 +60,14 @@ test_expect_success PERL 'git restore -p --source=HEAD^' '
        verify_state dir/foo parent index
 '
 
+test_expect_success PERL 'git restore -p --source=HEAD^...' '
+       set_state dir/foo work index &&
+       # the third n is to get out in case it mistakenly does not apply
+       test_write_lines n y n | git restore -p --source=HEAD^... &&
+       verify_saved_state bar &&
+       verify_state dir/foo parent index
+'
+
 test_expect_success PERL 'git restore -p handles deletion' '
        set_state dir/foo work index &&
        rm dir/foo &&
index f764b7e3f53e95e11acd4c2bdaafb737979d45e2..7cb7a7038277fdf5e55e984f998d56324d7c62cd 100755 (executable)
@@ -179,7 +179,8 @@ test_expect_success 'add -u resolves unmerged paths' '
 
 test_expect_success '"add -u non-existent" should fail' '
        test_must_fail git add -u non-existent &&
-       ! (git ls-files | grep "non-existent")
+       git ls-files >actual &&
+       ! grep "non-existent" actual
 '
 
 test_done
index 52585ec2aa8ba6d9104060361523c1b307bebfda..b85bd2655d8c6bc850062c8719585c4d954ed0a8 100755 (executable)
@@ -61,6 +61,16 @@ test_expect_success '"list" all worktrees --porcelain' '
        test_cmp expect actual
 '
 
+test_expect_success '"list" all worktress with locked annotation' '
+       test_when_finished "rm -rf locked unlocked out && git worktree prune" &&
+       git worktree add --detach locked master &&
+       git worktree add --detach unlocked master &&
+       git worktree lock locked &&
+       git worktree list >out &&
+       grep "/locked  *[0-9a-f].* locked$" out &&
+       ! grep "/unlocked  *[0-9a-f].* locked$" out
+'
+
 test_expect_success 'bare repo setup' '
        git init --bare bare1 &&
        echo "data" >file1 &&
index 6efe7a44bc628f413f965b379771e0376b605911..a0b832d59e611cd848a1bf5fc8d6334cbd5f3833 100755 (executable)
@@ -321,11 +321,11 @@ test_expect_success 'git branch --list -v with --abbrev' '
 
 '
 
-test_expect_success 'git branch --column' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'git branch --column' '
        COLUMNS=81 git branch --column=column >actual &&
        cat >expect <<\EOF &&
-  a/b/c    bam      foo      l      * master   n        o/p      r
-  abc      bar      j/k      m/m      mb       o/o      q        topic
+  a/b/c   bam     foo     l     * main    n       o/p     r
+  abc     bar     j/k     m/m     mb      o/o     q       topic
 EOF
        test_cmp expect actual
 '
@@ -358,15 +358,15 @@ EOF
        test_cmp expect actual
 '
 
-test_expect_success 'git branch with column.*' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'git branch with column.*' '
        git config column.ui column &&
        git config column.branch "dense" &&
        COLUMNS=80 git branch >actual &&
        git config --unset column.branch &&
        git config --unset column.ui &&
        cat >expect <<\EOF &&
-  a/b/c   bam   foo   l   * master   n     o/p   r
-  abc     bar   j/k   m/m   mb       o/o   q     topic
+  a/b/c   bam   foo   l   * main   n     o/p   r
+  abc     bar   j/k   m/m   mb     o/o   q     topic
 EOF
        test_cmp expect actual
 '
@@ -375,9 +375,9 @@ test_expect_success 'git branch --column -v should fail' '
        test_must_fail git branch --column -v
 '
 
-test_expect_success 'git branch -v with column.ui ignored' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'git branch -v with column.ui ignored' '
        git config column.ui column &&
-       COLUMNS=80 git branch -v | cut -c -9 | sed "s/ *$//" >actual &&
+       COLUMNS=80 git branch -v | cut -c -8 | sed "s/ *$//" >actual &&
        git config --unset column.ui &&
        cat >expect <<\EOF &&
   a/b/c
index efea5c49713888f12f19d693916553b8c9abd322..3733cd0091e5a44f059e402ec8283681356375ab 100755 (executable)
@@ -242,7 +242,7 @@ test_expect_success 'branch --merged combined with --no-merged' '
 # Here "topic" tracks "master" with one extra commit, and "zzz" points to the
 # same tip as master The name "zzz" must come alphabetically after "topic"
 # as we process them in that order.
-test_expect_success 'branch --merged with --verbose' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'branch --merged with --verbose' '
        git branch --track topic master &&
        git branch zzz topic &&
        git checkout topic &&
@@ -256,9 +256,9 @@ test_expect_success 'branch --merged with --verbose' '
        test_cmp expect actual &&
        git branch --verbose --merged topic >actual &&
        cat >expect <<-EOF &&
-         master $(git rev-parse --short master) second on master
-       * topic  $(git rev-parse --short topic ) [ahead 1] foo
-         zzz    $(git rev-parse --short zzz   ) second on master
+         main  $(git rev-parse --short main) second on main
+       * topic $(git rev-parse --short topic ) [ahead 1] foo
+         zzz   $(git rev-parse --short zzz   ) second on main
        EOF
        test_i18ncmp expect actual
 '
index 71818b90f00d3727cb00e24da181fc9dec420f08..d65586541d2cce7a7466cfe735eb87f831afd589 100755 (executable)
@@ -329,7 +329,7 @@ test_expect_success '--color overrides auto-color' '
        test_cmp expect.color actual
 '
 
-test_expect_success 'verbose output lists worktree path' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'verbose output lists worktree path' '
        one=$(git rev-parse --short HEAD) &&
        two=$(git rev-parse --short master) &&
        cat >expect <<-EOF &&
@@ -337,7 +337,7 @@ test_expect_success 'verbose output lists worktree path' '
          ambiguous                    $one one
          branch-one                   $two two
        + branch-two                   $one ($(pwd)/worktree_dir) one
-         master                       $two two
+         main                         $two two
          ref-to-branch                $two two
          ref-to-remote                $two two
        EOF
index 4f1e16bb44e279a0c5c0fc96f95eb285e4b22c3c..289625c4646464c412384c6c498383fe7e577da3 100755 (executable)
@@ -28,12 +28,12 @@ test_expect_success 'regular output shows colors' '
        test_cmp expect actual
 '
 
-test_expect_success 'verbose output shows colors' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'verbose output shows colors' '
        oid=$(git rev-parse --short HEAD) &&
        cat >expect <<-EOF &&
-       * <CYAN>master               <RESET> $oid foo
-         <BLUE>other                <RESET> $oid foo
-         <YELLOW>remotes/origin/master<RESET> $oid foo
+       * <CYAN>main               <RESET> $oid foo
+         <BLUE>other              <RESET> $oid foo
+         <YELLOW>remotes/origin/main<RESET> $oid foo
        EOF
        git branch --color -v -a >actual.raw &&
        test_decode_color <actual.raw >actual &&
index b47c59c190b37d179cba0d9bd316ca4ad96930b8..54120b09d6e9b6e29784393458671d715b06efc4 100755 (executable)
@@ -68,4 +68,60 @@ test_expect_failure 'rebase -p --no-gpg-sign override commit.gpgsign' '
        test_must_fail git verify-commit HEAD
 '
 
+test_expect_success 'rebase -r, merge strategy, --gpg-sign will sign commit' '
+       git reset --hard merged &&
+       test_unconfig commit.gpgsign &&
+       git rebase -fr --gpg-sign -s resolve --root &&
+       git verify-commit HEAD
+'
+
+test_expect_success 'rebase -r, merge strategy, commit.gpgsign=true will sign commit' '
+       git reset --hard merged &&
+       git config commit.gpgsign true &&
+       git rebase -fr -s resolve --root &&
+       git verify-commit HEAD
+'
+
+test_expect_success 'rebase -r, merge strategy, commit.gpgsign=false --gpg-sign will sign commit' '
+       git reset --hard merged &&
+       git config commit.gpgsign false &&
+       git rebase -fr --gpg-sign -s resolve --root &&
+       git verify-commit HEAD
+'
+
+test_expect_success "rebase -r, merge strategy, commit.gpgsign=true --no-gpg-sign won't sign commit" '
+       git reset --hard merged &&
+       git config commit.gpgsign true &&
+       git rebase -fr --no-gpg-sign -s resolve --root &&
+       test_must_fail git verify-commit HEAD
+'
+
+test_expect_success 'rebase -r --gpg-sign will sign commit' '
+       git reset --hard merged &&
+       test_unconfig commit.gpgsign &&
+       git rebase -fr --gpg-sign --root &&
+       git verify-commit HEAD
+'
+
+test_expect_success 'rebase -r with commit.gpgsign=true will sign commit' '
+       git reset --hard merged &&
+       git config commit.gpgsign true &&
+       git rebase -fr --root &&
+       git verify-commit HEAD
+'
+
+test_expect_success 'rebase -r --gpg-sign with commit.gpgsign=false will sign commit' '
+       git reset --hard merged &&
+       git config commit.gpgsign false &&
+       git rebase -fr --gpg-sign --root &&
+       git verify-commit HEAD
+'
+
+test_expect_success "rebase -r --no-gpg-sign with commit.gpgsign=true won't sign commit" '
+       git reset --hard merged &&
+       git config commit.gpgsign true &&
+       git rebase -fr --no-gpg-sign --root &&
+       test_must_fail git verify-commit HEAD
+'
+
 test_done
diff --git a/t/t3920-crlf-messages.sh b/t/t3920-crlf-messages.sh
new file mode 100755 (executable)
index 0000000..70ddce3
--- /dev/null
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+test_description='Test ref-filter and pretty APIs for commit and tag messages using CRLF'
+. ./test-lib.sh
+
+LIB_CRLF_BRANCHES=""
+
+create_crlf_ref () {
+       branch="$1" &&
+       cat >.crlf-orig-$branch.txt &&
+       cat .crlf-orig-$branch.txt | append_cr >.crlf-message-$branch.txt &&
+       grep 'Subject' .crlf-orig-$branch.txt | tr '\n' ' ' | sed 's/[ ]*$//' | tr -d '\n' >.crlf-subject-$branch.txt &&
+       grep 'Body' .crlf-message-$branch.txt >.crlf-body-$branch.txt || true &&
+       LIB_CRLF_BRANCHES="${LIB_CRLF_BRANCHES} ${branch}" &&
+       test_tick &&
+       hash=$(git commit-tree HEAD^{tree} -p HEAD -F .crlf-message-${branch}.txt) &&
+       git branch ${branch} ${hash} &&
+       git tag tag-${branch} ${branch} -F .crlf-message-${branch}.txt --cleanup=verbatim
+}
+
+create_crlf_refs () {
+       create_crlf_ref crlf <<-\EOF &&
+       Subject first line
+
+       Body first line
+       Body second line
+       EOF
+       create_crlf_ref crlf-empty-lines-after-subject <<-\EOF &&
+       Subject first line
+
+
+       Body first line
+       Body second line
+       EOF
+       create_crlf_ref crlf-two-line-subject <<-\EOF &&
+       Subject first line
+       Subject second line
+
+       Body first line
+       Body second line
+       EOF
+       create_crlf_ref crlf-two-line-subject-no-body <<-\EOF &&
+       Subject first line
+       Subject second line
+       EOF
+       create_crlf_ref crlf-two-line-subject-no-body-trailing-newline <<-\EOF
+       Subject first line
+       Subject second line
+
+       EOF
+}
+
+test_crlf_subject_body_and_contents() {
+       command_and_args="$@" &&
+       command=$1 &&
+       if test ${command} = "branch" || test ${command} = "for-each-ref" || test ${command} = "tag"
+       then
+               atoms="(contents:subject) (contents:body) (contents)"
+       elif test ${command} = "log" || test ${command} = "show"
+       then
+               atoms="s b B"
+       fi &&
+       files="subject body message" &&
+       while test -n "${atoms}"
+       do
+               set ${atoms} && atom=$1 && shift && atoms="$*" &&
+               set ${files} && file=$1 && shift && files="$*" &&
+               test_expect_success "${command}: --format='%${atom}' works with messages using CRLF" "
+                       rm -f expect &&
+                       for ref in ${LIB_CRLF_BRANCHES}
+                       do
+                               cat .crlf-${file}-\"\${ref}\".txt >>expect &&
+                               printf \"\n\" >>expect
+                       done &&
+                       git $command_and_args --format=\"%${atom}\" >actual &&
+                       test_cmp expect actual
+               "
+       done
+}
+
+
+test_expect_success 'Setup refs with commit and tag messages using CRLF' '
+       test_commit inital &&
+       create_crlf_refs
+'
+
+test_expect_success 'branch: --verbose works with messages using CRLF' '
+       rm -f expect &&
+       for branch in $LIB_CRLF_BRANCHES
+       do
+               printf "  " >>expect &&
+               cat .crlf-subject-${branch}.txt >>expect &&
+               printf "\n" >>expect
+       done &&
+       git branch -v >tmp &&
+       # Remove first two columns, and the line for the currently checked out branch
+       current=$(git branch --show-current) &&
+       grep -v $current <tmp | awk "{\$1=\$2=\"\"}1"  >actual &&
+       test_cmp expect actual
+'
+
+test_crlf_subject_body_and_contents branch --list crlf*
+
+test_crlf_subject_body_and_contents tag --list tag-crlf*
+
+test_crlf_subject_body_and_contents for-each-ref refs/heads/crlf*
+
+test_expect_success 'log: --oneline works with messages using CRLF' '
+       for branch in $LIB_CRLF_BRANCHES
+       do
+               cat .crlf-subject-${branch}.txt >expect &&
+               printf "\n" >>expect &&
+               git log --oneline -1 ${branch} >tmp-branch &&
+               git log --oneline -1 tag-${branch} >tmp-tag &&
+               cut -d" " -f2- <tmp-branch >actual-branch &&
+               cut -d" " -f2- <tmp-tag >actual-tag &&
+               test_cmp expect actual-branch &&
+               test_cmp expect actual-tag || return 1
+       done
+'
+
+test_crlf_subject_body_and_contents log --all --reverse --grep Subject
+
+test_crlf_subject_body_and_contents show $LIB_CRLF_BRANCHES
+
+test_done
index 5c7b0122b4f194d327d488d4b5dd6b8ad28e7673..f72d456d3bc32a18222a96790894648ec858f968 100755 (executable)
@@ -6,6 +6,7 @@
 test_description='Various diff formatting options'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 test_expect_success setup '
 
@@ -333,6 +334,7 @@ log -SF master --max-count=2
 log -GF master
 log -GF -p master
 log -GF -p --pickaxe-all master
+log -IA -IB -I1 -I2 -p master
 log --decorate --all
 log --decorate=full --all
 
@@ -473,4 +475,43 @@ test_expect_success 'diff-tree --stdin with log formatting' '
        test_cmp expect actual
 '
 
+test_expect_success 'diff -I<regex>: setup' '
+       git checkout master &&
+       test_seq 50 >file0 &&
+       git commit -m "Set up -I<regex> test file" file0 &&
+       test_seq 50 | sed -e "s/13/ten and three/" -e "/7\$/d" >file0 &&
+       echo >>file0
+'
+test_expect_success 'diff -I<regex>' '
+       git diff --ignore-blank-lines -I"ten.*e" -I"^[124-9]" >actual &&
+       cat >expect <<-\EOF &&
+       diff --git a/file0 b/file0
+       --- a/file0
+       +++ b/file0
+       @@ -34,7 +31,6 @@
+        34
+        35
+        36
+       -37
+        38
+        39
+        40
+       EOF
+       compare_diff_patch expect actual
+'
+
+test_expect_success 'diff -I<regex> --stat' '
+       git diff --stat --ignore-blank-lines -I"ten.*e" -I"^[124-9]" >actual &&
+       cat >expect <<-\EOF &&
+        file0 | 1 -
+        1 file changed, 1 deletion(-)
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'diff -I<regex>: detect malformed regex' '
+       test_expect_code 129 git diff --ignore-matching-lines="^[124-9" 2>error &&
+       test_i18ngrep "invalid regex given to -I: " error
+'
+
 test_done
diff --git a/t/t4013/diff.log_-IA_-IB_-I1_-I2_-p_master b/t/t4013/diff.log_-IA_-IB_-I1_-I2_-p_master
new file mode 100644 (file)
index 0000000..929f35a
--- /dev/null
@@ -0,0 +1,99 @@
+$ git log -IA -IB -I1 -I2 -p master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
index 9d077975791c1c002b4a927dfae71de594c8855f..9675bc17db276a51b1c5dc1b64ff1e94e1b0fc09 100755 (executable)
@@ -27,6 +27,7 @@ test_expect_success 'setup' '
 
 diffpatterns="
        ada
+       bash
        bibtex
        cpp
        csharp
diff --git a/t/t4018/bash-arithmetic-function b/t/t4018/bash-arithmetic-function
new file mode 100644 (file)
index 0000000..c0b276c
--- /dev/null
@@ -0,0 +1,4 @@
+RIGHT() ((
+
+    ChangeMe = "$x" + "$y"
+))
diff --git a/t/t4018/bash-bashism-style-compact b/t/t4018/bash-bashism-style-compact
new file mode 100644 (file)
index 0000000..1ca3126
--- /dev/null
@@ -0,0 +1,6 @@
+function RIGHT {
+    function InvalidSyntax{
+        :
+        echo 'ChangeMe'
+    }
+}
diff --git a/t/t4018/bash-bashism-style-function b/t/t4018/bash-bashism-style-function
new file mode 100644 (file)
index 0000000..f1de4fa
--- /dev/null
@@ -0,0 +1,4 @@
+function RIGHT {
+    :
+    echo 'ChangeMe'
+}
diff --git a/t/t4018/bash-bashism-style-whitespace b/t/t4018/bash-bashism-style-whitespace
new file mode 100644 (file)
index 0000000..ade85dd
--- /dev/null
@@ -0,0 +1,4 @@
+        function       RIGHT   (       )       {
+
+           ChangeMe
+        }
diff --git a/t/t4018/bash-conditional-function b/t/t4018/bash-conditional-function
new file mode 100644 (file)
index 0000000..c5949e8
--- /dev/null
@@ -0,0 +1,4 @@
+RIGHT() [[ \
+
+    "$a" > "$ChangeMe"
+]]
diff --git a/t/t4018/bash-missing-parentheses b/t/t4018/bash-missing-parentheses
new file mode 100644 (file)
index 0000000..8c8a05d
--- /dev/null
@@ -0,0 +1,6 @@
+function RIGHT {
+    functionInvalidSyntax {
+        :
+        echo 'ChangeMe'
+    }
+}
diff --git a/t/t4018/bash-mixed-style-compact b/t/t4018/bash-mixed-style-compact
new file mode 100644 (file)
index 0000000..d9364cb
--- /dev/null
@@ -0,0 +1,4 @@
+function RIGHT(){
+    :
+    echo 'ChangeMe'
+}
diff --git a/t/t4018/bash-mixed-style-function b/t/t4018/bash-mixed-style-function
new file mode 100644 (file)
index 0000000..555f9b2
--- /dev/null
@@ -0,0 +1,4 @@
+function RIGHT() {
+
+    ChangeMe
+}
diff --git a/t/t4018/bash-nested-functions b/t/t4018/bash-nested-functions
new file mode 100644 (file)
index 0000000..2c9237e
--- /dev/null
@@ -0,0 +1,6 @@
+outer() {
+    RIGHT() {
+        :
+        echo 'ChangeMe'
+    }
+}
diff --git a/t/t4018/bash-other-characters b/t/t4018/bash-other-characters
new file mode 100644 (file)
index 0000000..a3f390d
--- /dev/null
@@ -0,0 +1,4 @@
+_RIGHT_0n() {
+
+    ChangeMe
+}
diff --git a/t/t4018/bash-posix-style-compact b/t/t4018/bash-posix-style-compact
new file mode 100644 (file)
index 0000000..045bd20
--- /dev/null
@@ -0,0 +1,4 @@
+RIGHT(){
+
+    ChangeMe
+}
diff --git a/t/t4018/bash-posix-style-function b/t/t4018/bash-posix-style-function
new file mode 100644 (file)
index 0000000..a4d1448
--- /dev/null
@@ -0,0 +1,4 @@
+RIGHT() {
+
+    ChangeMe
+}
diff --git a/t/t4018/bash-posix-style-whitespace b/t/t4018/bash-posix-style-whitespace
new file mode 100644 (file)
index 0000000..4d984f0
--- /dev/null
@@ -0,0 +1,4 @@
+        RIGHT  (       )       {
+
+           ChangeMe
+        }
diff --git a/t/t4018/bash-subshell-function b/t/t4018/bash-subshell-function
new file mode 100644 (file)
index 0000000..80baa09
--- /dev/null
@@ -0,0 +1,4 @@
+RIGHT() (
+
+    ChangeMe=2
+)
diff --git a/t/t4018/bash-trailing-comment b/t/t4018/bash-trailing-comment
new file mode 100644 (file)
index 0000000..f1edbed
--- /dev/null
@@ -0,0 +1,4 @@
+RIGHT() { # Comment
+
+    ChangeMe
+}
diff --git a/t/t4018/css-attribute-value-selector b/t/t4018/css-attribute-value-selector
new file mode 100644 (file)
index 0000000..918256b
--- /dev/null
@@ -0,0 +1,4 @@
+[class*="RIGHT"] {
+    background : #000;
+    border : 10px ChangeMe #C6C6C6;
+}
diff --git a/t/t4018/css-block-level-@-statements b/t/t4018/css-block-level-@-statements
new file mode 100644 (file)
index 0000000..d6755f2
--- /dev/null
@@ -0,0 +1,10 @@
+@keyframes RIGHT {
+    from {
+        background : #000;
+        border : 10px ChangeMe #C6C6C6;
+    }
+    to {
+        background : #fff;
+        border : 10px solid #C6C6C6;
+    }
+}
diff --git a/t/t4018/css-class-selector b/t/t4018/css-class-selector
new file mode 100644 (file)
index 0000000..f790a00
--- /dev/null
@@ -0,0 +1,4 @@
+.RIGHT {
+    background : #000;
+    border : 10px ChangeMe #C6C6C6;
+}
diff --git a/t/t4018/css-id-selector b/t/t4018/css-id-selector
new file mode 100644 (file)
index 0000000..17c5111
--- /dev/null
@@ -0,0 +1,4 @@
+#RIGHT {
+    background : #000;
+    border : 10px ChangeMe #C6C6C6;
+}
diff --git a/t/t4018/css-root-selector b/t/t4018/css-root-selector
new file mode 100644 (file)
index 0000000..22b958e
--- /dev/null
@@ -0,0 +1,4 @@
+:RIGHT {
+    background : #000;
+    border : 10px ChangeMe #C6C6C6;
+}
diff --git a/t/t4018/php-abstract-method b/t/t4018/php-abstract-method
new file mode 100644 (file)
index 0000000..ce215df
--- /dev/null
@@ -0,0 +1,7 @@
+abstract class Klass
+{
+    abstract public function RIGHT(): ?string
+    {
+        return 'ChangeMe';
+    }
+}
diff --git a/t/t4018/php-final-method b/t/t4018/php-final-method
new file mode 100644 (file)
index 0000000..537fb8a
--- /dev/null
@@ -0,0 +1,7 @@
+class Klass
+{
+    final public function RIGHT(): string
+    {
+        return 'ChangeMe';
+    }
+}
diff --git a/t/t4018/rust-macro-rules b/t/t4018/rust-macro-rules
new file mode 100644 (file)
index 0000000..ec610c5
--- /dev/null
@@ -0,0 +1,6 @@
+macro_rules! RIGHT {
+    () => {
+        // a comment
+        let x = ChangeMe;
+    };
+}
diff --git a/t/t4068-diff-symmetric-merge-base.sh b/t/t4068-diff-symmetric-merge-base.sh
new file mode 100755 (executable)
index 0000000..03487cc
--- /dev/null
@@ -0,0 +1,193 @@
+#!/bin/sh
+
+test_description='behavior of diff with symmetric-diff setups and --merge-base'
+
+. ./test-lib.sh
+
+# build these situations:
+#  - normal merge with one merge base (br1...b2r);
+#  - criss-cross merge ie 2 merge bases (br1...master);
+#  - disjoint subgraph (orphan branch, br3...master).
+#
+#     B---E   <-- master
+#    / \ /
+#   A   X
+#    \ / \
+#     C---D--G   <-- br1
+#      \    /
+#       ---F   <-- br2
+#
+#  H  <-- br3
+#
+# We put files into a few commits so that we can verify the
+# output as well.
+
+test_expect_success setup '
+       git commit --allow-empty -m A &&
+       echo b >b &&
+       git add b &&
+       git commit -m B &&
+       git checkout -b br1 HEAD^ &&
+       echo c >c &&
+       git add c &&
+       git commit -m C &&
+       git tag commit-C &&
+       git merge -m D master &&
+       git tag commit-D &&
+       git checkout master &&
+       git merge -m E commit-C &&
+       git checkout -b br2 commit-C &&
+       echo f >f &&
+       git add f &&
+       git commit -m F &&
+       git checkout br1 &&
+       git merge -m G br2 &&
+       git checkout --orphan br3 &&
+       git commit -m H
+'
+
+test_expect_success 'diff with one merge base' '
+       git diff commit-D...br1 >tmp &&
+       tail -n 1 tmp >actual &&
+       echo +f >expect &&
+       test_cmp expect actual
+'
+
+# The output (in tmp) can have +b or +c depending
+# on which merge base (commit B or C) is picked.
+# It should have one of those two, which comes out
+# to seven lines.
+test_expect_success 'diff with two merge bases' '
+       git diff br1...master >tmp 2>err &&
+       test_line_count = 7 tmp &&
+       test_line_count = 1 err
+'
+
+test_expect_success 'diff with no merge bases' '
+       test_must_fail git diff br2...br3 2>err &&
+       test_i18ngrep "fatal: br2...br3: no merge base" err
+'
+
+test_expect_success 'diff with too many symmetric differences' '
+       test_must_fail git diff br1...master br2...br3 2>err &&
+       test_i18ngrep "usage" err
+'
+
+test_expect_success 'diff with symmetric difference and extraneous arg' '
+       test_must_fail git diff master br1...master 2>err &&
+       test_i18ngrep "usage" err
+'
+
+test_expect_success 'diff with two ranges' '
+       test_must_fail git diff master br1..master br2..br3 2>err &&
+       test_i18ngrep "usage" err
+'
+
+test_expect_success 'diff with ranges and extra arg' '
+       test_must_fail git diff master br1..master commit-D 2>err &&
+       test_i18ngrep "usage" err
+'
+
+test_expect_success 'diff --merge-base with no commits' '
+       test_must_fail git diff --merge-base
+'
+
+test_expect_success 'diff --merge-base with three commits' '
+       test_must_fail git diff --merge-base br1 br2 master 2>err &&
+       test_i18ngrep "usage" err
+'
+
+for cmd in diff-index diff
+do
+       test_expect_success "$cmd --merge-base with one commit" '
+               git checkout master &&
+               git $cmd commit-C >expect &&
+               git $cmd --merge-base br2 >actual &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "$cmd --merge-base with one commit and unstaged changes" '
+               git checkout master &&
+               test_when_finished git reset --hard &&
+               echo unstaged >>c &&
+               git $cmd commit-C >expect &&
+               git $cmd --merge-base br2 >actual &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "$cmd --merge-base with one commit and staged and unstaged changes" '
+               git checkout master &&
+               test_when_finished git reset --hard &&
+               echo staged >>c &&
+               git add c &&
+               echo unstaged >>c &&
+               git $cmd commit-C >expect &&
+               git $cmd --merge-base br2 >actual &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "$cmd --merge-base --cached with one commit and staged and unstaged changes" '
+               git checkout master &&
+               test_when_finished git reset --hard &&
+               echo staged >>c &&
+               git add c &&
+               echo unstaged >>c &&
+               git $cmd --cached commit-C >expect &&
+               git $cmd --cached --merge-base br2 >actual &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "$cmd --merge-base with non-commit" '
+               git checkout master &&
+               test_must_fail git $cmd --merge-base master^{tree} 2>err &&
+               test_i18ngrep "fatal: --merge-base only works with commits" err
+       '
+
+       test_expect_success "$cmd --merge-base with no merge bases and one commit" '
+               git checkout master &&
+               test_must_fail git $cmd --merge-base br3 2>err &&
+               test_i18ngrep "fatal: no merge base found" err
+       '
+
+       test_expect_success "$cmd --merge-base with multiple merge bases and one commit" '
+               git checkout master &&
+               test_must_fail git $cmd --merge-base br1 2>err &&
+               test_i18ngrep "fatal: multiple merge bases found" err
+       '
+done
+
+for cmd in diff-tree diff
+do
+       test_expect_success "$cmd --merge-base with two commits" '
+               git $cmd commit-C master >expect &&
+               git $cmd --merge-base br2 master >actual &&
+               test_cmp expect actual
+       '
+
+       test_expect_success "$cmd --merge-base commit and non-commit" '
+               test_must_fail git $cmd --merge-base br2 master^{tree} 2>err &&
+               test_i18ngrep "fatal: --merge-base only works with commits" err
+       '
+
+       test_expect_success "$cmd --merge-base with no merge bases and two commits" '
+               test_must_fail git $cmd --merge-base br2 br3 2>err &&
+               test_i18ngrep "fatal: no merge base found" err
+       '
+
+       test_expect_success "$cmd --merge-base with multiple merge bases and two commits" '
+               test_must_fail git $cmd --merge-base master br1 2>err &&
+               test_i18ngrep "fatal: multiple merge bases found" err
+       '
+done
+
+test_expect_success 'diff-tree --merge-base with one commit' '
+       test_must_fail git diff-tree --merge-base master 2>err &&
+       test_i18ngrep "fatal: --merge-base only works with two commits" err
+'
+
+test_expect_success 'diff --merge-base with range' '
+       test_must_fail git diff --merge-base br2..br3 2>err &&
+       test_i18ngrep "fatal: --merge-base does not work with ranges" err
+'
+
+test_done
diff --git a/t/t4068-diff-symmetric.sh b/t/t4068-diff-symmetric.sh
deleted file mode 100755 (executable)
index 31d17a5..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/bin/sh
-
-test_description='behavior of diff with symmetric-diff setups'
-
-. ./test-lib.sh
-
-# build these situations:
-#  - normal merge with one merge base (br1...b2r);
-#  - criss-cross merge ie 2 merge bases (br1...master);
-#  - disjoint subgraph (orphan branch, br3...master).
-#
-#     B---E   <-- master
-#    / \ /
-#   A   X
-#    \ / \
-#     C---D--G   <-- br1
-#      \    /
-#       ---F   <-- br2
-#
-#  H  <-- br3
-#
-# We put files into a few commits so that we can verify the
-# output as well.
-
-test_expect_success setup '
-       git commit --allow-empty -m A &&
-       echo b >b &&
-       git add b &&
-       git commit -m B &&
-       git checkout -b br1 HEAD^ &&
-       echo c >c &&
-       git add c &&
-       git commit -m C &&
-       git tag commit-C &&
-       git merge -m D master &&
-       git tag commit-D &&
-       git checkout master &&
-       git merge -m E commit-C &&
-       git checkout -b br2 commit-C &&
-       echo f >f &&
-       git add f &&
-       git commit -m F &&
-       git checkout br1 &&
-       git merge -m G br2 &&
-       git checkout --orphan br3 &&
-       git commit -m H
-'
-
-test_expect_success 'diff with one merge base' '
-       git diff commit-D...br1 >tmp &&
-       tail -n 1 tmp >actual &&
-       echo +f >expect &&
-       test_cmp expect actual
-'
-
-# The output (in tmp) can have +b or +c depending
-# on which merge base (commit B or C) is picked.
-# It should have one of those two, which comes out
-# to seven lines.
-test_expect_success 'diff with two merge bases' '
-       git diff br1...master >tmp 2>err &&
-       test_line_count = 7 tmp &&
-       test_line_count = 1 err
-'
-
-test_expect_success 'diff with no merge bases' '
-       test_must_fail git diff br2...br3 >tmp 2>err &&
-       test_i18ngrep "fatal: br2...br3: no merge base" err
-'
-
-test_expect_success 'diff with too many symmetric differences' '
-       test_must_fail git diff br1...master br2...br3 >tmp 2>err &&
-       test_i18ngrep "usage" err
-'
-
-test_expect_success 'diff with symmetric difference and extraneous arg' '
-       test_must_fail git diff master br1...master >tmp 2>err &&
-       test_i18ngrep "usage" err
-'
-
-test_expect_success 'diff with two ranges' '
-       test_must_fail git diff master br1..master br2..br3 >tmp 2>err &&
-       test_i18ngrep "usage" err
-'
-
-test_expect_success 'diff with ranges and extra arg' '
-       test_must_fail git diff master br1..master commit-D >tmp 2>err &&
-       test_i18ngrep "usage" err
-'
-
-test_done
index ebadbc347fc4fd9d435a366e46e3e1cebba81b83..da3e64f8110d54d7243c017a9a614baa7098142f 100755 (executable)
@@ -88,6 +88,13 @@ test_expect_success 'symlink becomes file' '
        '
 test_debug 'cat patch'
 
+test_expect_success 'symlink becomes file, in reverse' '
+       git checkout -f foo-symlinked-to-bar &&
+       git diff-tree -p HEAD foo-back-to-file > patch &&
+       git checkout foo-back-to-file &&
+       git apply -R --index < patch
+       '
+
 test_expect_success 'binary file becomes symlink' '
        git checkout -f foo-becomes-binary &&
        git diff-tree -p --binary HEAD foo-symlinked-to-bar > patch &&
index 972946c174c18ee831d8595068c6ffa235c8a538..305b7e649eb7a123556d89deae0c34ec91265905 100755 (executable)
@@ -32,6 +32,10 @@ test_expect_success 'apply same filename with independent changes' '
 
 test_expect_success 'apply same filename with overlapping changes' '
        git reset --hard &&
+
+       # Store same_fn so that we can check apply -R in next test
+       cp same_fn same_fn1 &&
+
        modify "s/^d/z/" same_fn &&
        git diff > patch0 &&
        git add same_fn &&
@@ -43,6 +47,11 @@ test_expect_success 'apply same filename with overlapping changes' '
        test_cmp same_fn same_fn2
 '
 
+test_expect_success 'apply same filename with overlapping changes, in reverse' '
+       git apply -R patch0 &&
+       test_cmp same_fn same_fn1
+'
+
 test_expect_success 'apply same new filename after rename' '
        git reset --hard &&
        git mv same_fn new_fn &&
index f340b376bca55f0aa7af191f91527f8da076bf19..ace469c95c2faaf91798639ab3d686d1637f7352 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='multi-pack-indexes'
 . ./test-lib.sh
 
+GIT_TEST_MULTI_PACK_INDEX=0
 objdir=.git/objects
 
 HASH_LEN=$(test_oid rawsz)
@@ -173,12 +174,12 @@ test_expect_success 'write progress off for redirected stderr' '
 '
 
 test_expect_success 'write force progress on for stderr' '
-       git multi-pack-index --object-dir=$objdir --progress write 2>err &&
+       GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --progress write 2>err &&
        test_file_not_empty err
 '
 
 test_expect_success 'write with the --no-progress option' '
-       git multi-pack-index --object-dir=$objdir --no-progress write 2>err &&
+       GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --no-progress write 2>err &&
        test_line_count = 0 err
 '
 
@@ -368,17 +369,17 @@ test_expect_success 'git-fsck incorrect offset' '
 '
 
 test_expect_success 'repack progress off for redirected stderr' '
-       git multi-pack-index --object-dir=$objdir repack 2>err &&
+       GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir repack 2>err &&
        test_line_count = 0 err
 '
 
 test_expect_success 'repack force progress on for stderr' '
-       git multi-pack-index --object-dir=$objdir --progress repack 2>err &&
+       GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --progress repack 2>err &&
        test_file_not_empty err
 '
 
 test_expect_success 'repack with the --no-progress option' '
-       git multi-pack-index --object-dir=$objdir --no-progress repack 2>err &&
+       GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --no-progress repack 2>err &&
        test_line_count = 0 err
 '
 
@@ -562,7 +563,7 @@ test_expect_success 'expire progress off for redirected stderr' '
 test_expect_success 'expire force progress on for stderr' '
        (
                cd dup &&
-               git multi-pack-index --progress expire 2>err &&
+               GIT_PROGRESS_DELAY=0 git multi-pack-index --progress expire 2>err &&
                test_file_not_empty err
        )
 '
@@ -570,7 +571,7 @@ test_expect_success 'expire force progress on for stderr' '
 test_expect_success 'expire with the --no-progress option' '
        (
                cd dup &&
-               git multi-pack-index --no-progress expire 2>err &&
+               GIT_PROGRESS_DELAY=0 git multi-pack-index --no-progress expire 2>err &&
                test_line_count = 0 err
        )
 '
index c334ee9155b12c91b41ac86722e8f666e7b62d0e..4d3842b83b9456362a08842ebc10614d02f0943d 100755 (executable)
@@ -440,4 +440,17 @@ test_expect_success '--split=replace with partial Bloom data' '
        verify_chain_files_exist $graphdir
 '
 
+test_expect_success 'prevent regression for duplicate commits across layers' '
+       git init dup &&
+       git -C dup commit --allow-empty -m one &&
+       git -C dup -c core.commitGraph=false commit-graph write --split=no-merge --reachable 2>err &&
+       test_i18ngrep "attempting to write a commit-graph" err &&
+       git -C dup commit-graph write --split=no-merge --reachable &&
+       git -C dup commit --allow-empty -m two &&
+       git -C dup commit-graph write --split=no-merge --reachable &&
+       git -C dup commit --allow-empty -m three &&
+       git -C dup commit-graph write --split --reachable &&
+       git -C dup commit-graph verify
+'
+
 test_done
index 8d62edd98b5c87995f5f0697b4b7198c6da58c0d..eaa6e9022048ade20043abdbecc40ba78f51b5bf 100755 (executable)
@@ -145,8 +145,8 @@ test_expect_success 'remove remote protects local branches' '
 test_expect_success 'remove errors out early when deleting non-existent branch' '
        (
                cd test &&
-               echo "fatal: No such remote: '\''foo'\''" >expect &&
-               test_must_fail git remote rm foo 2>actual &&
+               echo "error: No such remote: '\''foo'\''" >expect &&
+               test_expect_code 2 git remote rm foo 2>actual &&
                test_i18ncmp expect actual
        )
 '
@@ -173,24 +173,37 @@ test_expect_success 'remove remote with a branch without configured merge' '
 test_expect_success 'rename errors out early when deleting non-existent branch' '
        (
                cd test &&
-               echo "fatal: No such remote: '\''foo'\''" >expect &&
-               test_must_fail git remote rename foo bar 2>actual &&
+               echo "error: No such remote: '\''foo'\''" >expect &&
+               test_expect_code 2 git remote rename foo bar 2>actual &&
                test_i18ncmp expect actual
        )
 '
 
+test_expect_success 'rename errors out early when when new name is invalid' '
+       test_config remote.foo.vcs bar &&
+       echo "fatal: '\''invalid...name'\'' is not a valid remote name" >expect &&
+       test_must_fail git remote rename foo invalid...name 2>actual &&
+       test_i18ncmp expect actual
+'
+
 test_expect_success 'add existing foreign_vcs remote' '
        test_config remote.foo.vcs bar &&
-       echo "fatal: remote foo already exists." >expect &&
-       test_must_fail git remote add foo bar 2>actual &&
+       echo "error: remote foo already exists." >expect &&
+       test_expect_code 3 git remote add foo bar 2>actual &&
        test_i18ncmp expect actual
 '
 
 test_expect_success 'add existing foreign_vcs remote' '
        test_config remote.foo.vcs bar &&
        test_config remote.bar.vcs bar &&
-       echo "fatal: remote bar already exists." >expect &&
-       test_must_fail git remote rename foo bar 2>actual &&
+       echo "error: remote bar already exists." >expect &&
+       test_expect_code 3 git remote rename foo bar 2>actual &&
+       test_i18ncmp expect actual
+'
+
+test_expect_success 'add invalid foreign_vcs remote' '
+       echo "fatal: '\''invalid...name'\'' is not a valid remote name" >expect &&
+       test_must_fail git remote add invalid...name bar 2>actual &&
        test_i18ncmp expect actual
 '
 
@@ -200,28 +213,28 @@ cat >test/expect <<EOF
   Push  URL: $(pwd)/one
   HEAD branch: master
   Remote branches:
-    master new (next fetch will store in remotes/origin)
-    side   tracked
+    main new (next fetch will store in remotes/origin)
+    side tracked
   Local branches configured for 'git pull':
-    ahead    merges with remote master
-    master   merges with remote master
+    ahead    merges with remote main
+    main     merges with remote main
     octopus  merges with remote topic-a
                 and with remote topic-b
                 and with remote topic-c
     rebase  rebases onto remote master
   Local refs configured for 'git push':
-    master pushes to master   (local out of date)
-    master pushes to upstream (create)
+    main pushes to main     (local out of date)
+    main pushes to upstream (create)
 * remote two
   Fetch URL: ../two
   Push  URL: ../three
   HEAD branch: master
   Local refs configured for 'git push':
-    ahead  forces to master  (fast-forwardable)
-    master pushes to another (up to date)
+    ahead forces to main    (fast-forwardable)
+    main  pushes to another (up to date)
 EOF
 
-test_expect_success 'show' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'show' '
        (
                cd test &&
                git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
@@ -264,15 +277,15 @@ cat >test/expect <<EOF
     master
     side
   Local branches configured for 'git pull':
-    ahead  merges with remote master
-    master merges with remote master
+    ahead merges with remote main
+    main  merges with remote main
   Local refs configured for 'git push' (status not queried):
     (matching)           pushes to (matching)
-    refs/heads/master    pushes to refs/heads/upstream
+    refs/heads/main      pushes to refs/heads/upstream
     refs/tags/lastbackup forces to refs/tags/lastbackup
 EOF
 
-test_expect_success 'show -n' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'show -n' '
        mv one one.unreachable &&
        (
                cd test &&
@@ -315,7 +328,7 @@ test_expect_success 'set-head --auto' '
        )
 '
 
-test_expect_success 'set-head --auto has no problem w/multiple HEADs' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'set-head --auto has no problem w/multiple HEADs' '
        (
                cd test &&
                git fetch two "refs/heads/*:refs/remotes/two/*" &&
@@ -1335,7 +1348,7 @@ test_expect_success 'unqualified <dst> refspec DWIM and advice' '
        )
 '
 
-test_expect_success 'refs/remotes/* <src> refspec and unqualified <dst> DWIM and advice' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'refs/remotes/* <src> refspec and unqualified <dst> DWIM and advice' '
        (
                cd two &&
                git tag -a -m "Some tag" my-tag master &&
index dbc724e4c05836cb7fef21fdfae0fddcb069a69c..5d673358f9b79cda723a7013542bd8e7704ceb9c 100755 (executable)
@@ -942,7 +942,7 @@ test_expect_success 'fetching with auto-gc does not lock up' '
        )
 '
 
-test_expect_success C_LOCALE_OUTPUT 'fetch aligned output' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH,C_LOCALE_OUTPUT 'fetch aligned output' '
        git clone . full-output &&
        test_commit looooooooooooong-tag &&
        (
@@ -951,13 +951,13 @@ test_expect_success C_LOCALE_OUTPUT 'fetch aligned output' '
                grep -e "->" actual | cut -c 22- >../actual
        ) &&
        cat >expect <<-\EOF &&
-       master               -> origin/master
+       main                 -> origin/main
        looooooooooooong-tag -> looooooooooooong-tag
        EOF
        test_cmp expect actual
 '
 
-test_expect_success C_LOCALE_OUTPUT 'fetch compact output' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH,C_LOCALE_OUTPUT 'fetch compact output' '
        git clone . compact &&
        test_commit extraaa &&
        (
@@ -966,7 +966,7 @@ test_expect_success C_LOCALE_OUTPUT 'fetch compact output' '
                grep -e "->" actual | cut -c 22- >../actual
        ) &&
        cat >expect <<-\EOF &&
-       master     -> origin/*
+       main       -> origin/*
        extraaa    -> *
        EOF
        test_cmp expect actual
index 63205dfdf962dc31c9db5ba038674e12760a9909..dd8e423d253b76bc3ee45dfece170423d3299a33 100755 (executable)
@@ -18,7 +18,7 @@ add_upstream_commit() {
                head2=$(git rev-parse --short HEAD) &&
                echo "Fetching submodule submodule" > ../expect.err &&
                echo "From $pwd/submodule" >> ../expect.err &&
-               echo "   $head1..$head2  master     -> origin/master" >> ../expect.err
+               echo "   $head1..$head2  main       -> origin/main" >> ../expect.err
        ) &&
        (
                cd deepsubmodule &&
@@ -30,7 +30,7 @@ add_upstream_commit() {
                head2=$(git rev-parse --short HEAD) &&
                echo "Fetching submodule submodule/subdir/deepsubmodule" >> ../expect.err
                echo "From $pwd/deepsubmodule" >> ../expect.err &&
-               echo "   $head1..$head2  master     -> origin/master" >> ../expect.err
+               echo "   $head1..$head2  main       -> origin/main" >> ../expect.err
        )
 }
 
@@ -61,7 +61,7 @@ test_expect_success setup '
        )
 '
 
-test_expect_success "fetch --recurse-submodules recurses into submodules" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "fetch --recurse-submodules recurses into submodules" '
        add_upstream_commit &&
        (
                cd downstream &&
@@ -71,7 +71,7 @@ test_expect_success "fetch --recurse-submodules recurses into submodules" '
        test_i18ncmp expect.err actual.err
 '
 
-test_expect_success "submodule.recurse option triggers recursive fetch" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "submodule.recurse option triggers recursive fetch" '
        add_upstream_commit &&
        (
                cd downstream &&
@@ -81,7 +81,7 @@ test_expect_success "submodule.recurse option triggers recursive fetch" '
        test_i18ncmp expect.err actual.err
 '
 
-test_expect_success "fetch --recurse-submodules -j2 has the same output behaviour" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "fetch --recurse-submodules -j2 has the same output behaviour" '
        add_upstream_commit &&
        (
                cd downstream &&
@@ -111,7 +111,7 @@ test_expect_success "fetch --no-recurse-submodules only fetches superproject" '
        test_must_be_empty actual.err
 '
 
-test_expect_success "using fetchRecurseSubmodules=true in .gitmodules recurses into submodules" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "using fetchRecurseSubmodules=true in .gitmodules recurses into submodules" '
        (
                cd downstream &&
                git config -f .gitmodules submodule.submodule.fetchRecurseSubmodules true &&
@@ -141,7 +141,7 @@ test_expect_success "using fetchRecurseSubmodules=false in .git/config overrides
        test_must_be_empty actual.err
 '
 
-test_expect_success "--recurse-submodules overrides fetchRecurseSubmodules setting from .git/config" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "--recurse-submodules overrides fetchRecurseSubmodules setting from .git/config" '
        (
                cd downstream &&
                git fetch --recurse-submodules >../actual.out 2>../actual.err &&
@@ -170,7 +170,7 @@ test_expect_success "--quiet propagates to parallel submodules" '
        test_must_be_empty actual.err
 '
 
-test_expect_success "--dry-run propagates to submodules" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "--dry-run propagates to submodules" '
        add_upstream_commit &&
        (
                cd downstream &&
@@ -180,7 +180,7 @@ test_expect_success "--dry-run propagates to submodules" '
        test_i18ncmp expect.err actual.err
 '
 
-test_expect_success "Without --dry-run propagates to submodules" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "Without --dry-run propagates to submodules" '
        (
                cd downstream &&
                git fetch --recurse-submodules >../actual.out 2>../actual.err
@@ -189,7 +189,7 @@ test_expect_success "Without --dry-run propagates to submodules" '
        test_i18ncmp expect.err actual.err
 '
 
-test_expect_success "recurseSubmodules=true propagates into submodules" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "recurseSubmodules=true propagates into submodules" '
        add_upstream_commit &&
        (
                cd downstream &&
@@ -200,7 +200,7 @@ test_expect_success "recurseSubmodules=true propagates into submodules" '
        test_i18ncmp expect.err actual.err
 '
 
-test_expect_success "--recurse-submodules overrides config in submodule" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "--recurse-submodules overrides config in submodule" '
        add_upstream_commit &&
        (
                cd downstream &&
@@ -225,7 +225,7 @@ test_expect_success "--no-recurse-submodules overrides config setting" '
        test_must_be_empty actual.err
 '
 
-test_expect_success "Recursion doesn't happen when no new commits are fetched in the superproject" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion doesn't happen when no new commits are fetched in the superproject" '
        (
                cd downstream &&
                (
@@ -239,13 +239,13 @@ test_expect_success "Recursion doesn't happen when no new commits are fetched in
        test_must_be_empty actual.err
 '
 
-test_expect_success "Recursion stops when no new submodule commits are fetched" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion stops when no new submodule commits are fetched" '
        head1=$(git rev-parse --short HEAD) &&
        git add submodule &&
        git commit -m "new submodule" &&
        head2=$(git rev-parse --short HEAD) &&
        echo "From $pwd/." > expect.err.sub &&
-       echo "   $head1..$head2  master     -> origin/master" >>expect.err.sub &&
+       echo "   $head1..$head2  main       -> origin/main" >>expect.err.sub &&
        head -3 expect.err >> expect.err.sub &&
        (
                cd downstream &&
@@ -255,7 +255,7 @@ test_expect_success "Recursion stops when no new submodule commits are fetched"
        test_must_be_empty actual.out
 '
 
-test_expect_success "Recursion doesn't happen when new superproject commits don't change any submodules" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion doesn't happen when new superproject commits don't change any submodules" '
        add_upstream_commit &&
        head1=$(git rev-parse --short HEAD) &&
        echo a > file &&
@@ -263,7 +263,7 @@ test_expect_success "Recursion doesn't happen when new superproject commits don'
        git commit -m "new file" &&
        head2=$(git rev-parse --short HEAD) &&
        echo "From $pwd/." > expect.err.file &&
-       echo "   $head1..$head2  master     -> origin/master" >> expect.err.file &&
+       echo "   $head1..$head2  main       -> origin/main" >> expect.err.file &&
        (
                cd downstream &&
                git fetch >../actual.out 2>../actual.err
@@ -272,7 +272,7 @@ test_expect_success "Recursion doesn't happen when new superproject commits don'
        test_i18ncmp expect.err.file actual.err
 '
 
-test_expect_success "Recursion picks up config in submodule" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion picks up config in submodule" '
        (
                cd downstream &&
                git fetch --recurse-submodules &&
@@ -287,7 +287,7 @@ test_expect_success "Recursion picks up config in submodule" '
        git commit -m "new submodule" &&
        head2=$(git rev-parse --short HEAD) &&
        echo "From $pwd/." > expect.err.sub &&
-       echo "   $head1..$head2  master     -> origin/master" >> expect.err.sub &&
+       echo "   $head1..$head2  main       -> origin/main" >> expect.err.sub &&
        cat expect.err >> expect.err.sub &&
        (
                cd downstream &&
@@ -301,7 +301,7 @@ test_expect_success "Recursion picks up config in submodule" '
        test_must_be_empty actual.out
 '
 
-test_expect_success "Recursion picks up all submodules when necessary" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "Recursion picks up all submodules when necessary" '
        add_upstream_commit &&
        (
                cd submodule &&
@@ -316,14 +316,14 @@ test_expect_success "Recursion picks up all submodules when necessary" '
                head2=$(git rev-parse --short HEAD) &&
                echo "Fetching submodule submodule" > ../expect.err.sub &&
                echo "From $pwd/submodule" >> ../expect.err.sub &&
-               echo "   $head1..$head2  master     -> origin/master" >> ../expect.err.sub
+               echo "   $head1..$head2  main       -> origin/main" >> ../expect.err.sub
        ) &&
        head1=$(git rev-parse --short HEAD) &&
        git add submodule &&
        git commit -m "new submodule" &&
        head2=$(git rev-parse --short HEAD) &&
        echo "From $pwd/." > expect.err.2 &&
-       echo "   $head1..$head2  master     -> origin/master" >> expect.err.2 &&
+       echo "   $head1..$head2  main       -> origin/main" >> expect.err.2 &&
        cat expect.err.sub >> expect.err.2 &&
        tail -3 expect.err >> expect.err.2 &&
        (
@@ -334,7 +334,7 @@ test_expect_success "Recursion picks up all submodules when necessary" '
        test_must_be_empty actual.out
 '
 
-test_expect_success "'--recurse-submodules=on-demand' doesn't recurse when no new commits are fetched in the superproject (and ignores config)" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "'--recurse-submodules=on-demand' doesn't recurse when no new commits are fetched in the superproject (and ignores config)" '
        add_upstream_commit &&
        (
                cd submodule &&
@@ -349,7 +349,7 @@ test_expect_success "'--recurse-submodules=on-demand' doesn't recurse when no ne
                head2=$(git rev-parse --short HEAD) &&
                echo Fetching submodule submodule > ../expect.err.sub &&
                echo "From $pwd/submodule" >> ../expect.err.sub &&
-               echo "   $head1..$head2  master     -> origin/master" >> ../expect.err.sub
+               echo "   $head1..$head2  main       -> origin/main" >> ../expect.err.sub
        ) &&
        (
                cd downstream &&
@@ -361,14 +361,14 @@ test_expect_success "'--recurse-submodules=on-demand' doesn't recurse when no ne
        test_must_be_empty actual.err
 '
 
-test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necessary (and ignores config)" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "'--recurse-submodules=on-demand' recurses as deep as necessary (and ignores config)" '
        head1=$(git rev-parse --short HEAD) &&
        git add submodule &&
        git commit -m "new submodule" &&
        head2=$(git rev-parse --short HEAD) &&
        tail -3 expect.err > expect.err.deepsub &&
        echo "From $pwd/." > expect.err &&
-       echo "   $head1..$head2  master     -> origin/master" >>expect.err &&
+       echo "   $head1..$head2  main       -> origin/main" >>expect.err &&
        cat expect.err.sub >> expect.err &&
        cat expect.err.deepsub >> expect.err &&
        (
@@ -389,7 +389,7 @@ test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necess
        test_i18ncmp expect.err actual.err
 '
 
-test_expect_success "'--recurse-submodules=on-demand' stops when no new submodule commits are found in the superproject (and ignores config)" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "'--recurse-submodules=on-demand' stops when no new submodule commits are found in the superproject (and ignores config)" '
        add_upstream_commit &&
        head1=$(git rev-parse --short HEAD) &&
        echo a >> file &&
@@ -397,7 +397,7 @@ test_expect_success "'--recurse-submodules=on-demand' stops when no new submodul
        git commit -m "new file" &&
        head2=$(git rev-parse --short HEAD) &&
        echo "From $pwd/." > expect.err.file &&
-       echo "   $head1..$head2  master     -> origin/master" >> expect.err.file &&
+       echo "   $head1..$head2  main       -> origin/main" >> expect.err.file &&
        (
                cd downstream &&
                git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err
@@ -406,7 +406,7 @@ test_expect_success "'--recurse-submodules=on-demand' stops when no new submodul
        test_i18ncmp expect.err.file actual.err
 '
 
-test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "'fetch.recurseSubmodules=on-demand' overrides global config" '
        (
                cd downstream &&
                git fetch --recurse-submodules
@@ -418,7 +418,7 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config
        git commit -m "new submodule" &&
        head2=$(git rev-parse --short HEAD) &&
        echo "From $pwd/." > expect.err.2 &&
-       echo "   $head1..$head2  master     -> origin/master" >>expect.err.2 &&
+       echo "   $head1..$head2  main       -> origin/main" >>expect.err.2 &&
        head -3 expect.err >> expect.err.2 &&
        (
                cd downstream &&
@@ -434,7 +434,7 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config
        test_i18ncmp expect.err.2 actual.err
 '
 
-test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' overrides fetch.recurseSubmodules" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "'submodule.<sub>.fetchRecurseSubmodules=on-demand' overrides fetch.recurseSubmodules" '
        (
                cd downstream &&
                git fetch --recurse-submodules
@@ -446,7 +446,7 @@ test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' override
        git commit -m "new submodule" &&
        head2=$(git rev-parse --short HEAD) &&
        echo "From $pwd/." > expect.err.2 &&
-       echo "   $head1..$head2  master     -> origin/master" >>expect.err.2 &&
+       echo "   $head1..$head2  main       -> origin/main" >>expect.err.2 &&
        head -3 expect.err >> expect.err.2 &&
        (
                cd downstream &&
@@ -462,7 +462,7 @@ test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' override
        test_i18ncmp expect.err.2 actual.err
 '
 
-test_expect_success "don't fetch submodule when newly recorded commits are already present" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "don't fetch submodule when newly recorded commits are already present" '
        (
                cd submodule &&
                git checkout -q HEAD^^
@@ -472,7 +472,7 @@ test_expect_success "don't fetch submodule when newly recorded commits are alrea
        git commit -m "submodule rewound" &&
        head2=$(git rev-parse --short HEAD) &&
        echo "From $pwd/." > expect.err &&
-       echo "   $head1..$head2  master     -> origin/master" >> expect.err &&
+       echo "   $head1..$head2  main       -> origin/main" >> expect.err &&
        (
                cd downstream &&
                git fetch >../actual.out 2>../actual.err
@@ -485,7 +485,7 @@ test_expect_success "don't fetch submodule when newly recorded commits are alrea
        )
 '
 
-test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .gitmodules entry" '
+test_expect_success PREPARE_FOR_MAIN_BRANCH "'fetch.recurseSubmodules=on-demand' works also without .gitmodules entry" '
        (
                cd downstream &&
                git fetch --recurse-submodules
@@ -497,7 +497,7 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .git
        git commit -m "new submodule without .gitmodules" &&
        head2=$(git rev-parse --short HEAD) &&
        echo "From $pwd/." >expect.err.2 &&
-       echo "   $head1..$head2  master     -> origin/master" >>expect.err.2 &&
+       echo "   $head1..$head2  main       -> origin/main" >>expect.err.2 &&
        head -3 expect.err >>expect.err.2 &&
        (
                cd downstream &&
index 0b0eb1d0259f314261c131db8e089e6859a7f3ba..7813e8470e550a2eaf19943552fa69dfd4501747 100755 (executable)
@@ -13,6 +13,46 @@ setup_srcdst_basic () {
        )
 }
 
+# For tests with "--force-if-includes".
+setup_src_dup_dst () {
+       rm -fr src dup dst &&
+       git init --bare dst &&
+       git clone --no-local dst src &&
+       git clone --no-local dst dup
+       (
+               cd src &&
+               test_commit A &&
+               test_commit B &&
+               test_commit C &&
+               git push origin
+       ) &&
+       (
+               cd dup &&
+               git fetch &&
+               git merge origin/master &&
+               git switch -c branch master~2 &&
+               test_commit D &&
+               test_commit E &&
+               git push origin --all
+       ) &&
+       (
+               cd src &&
+               git switch master &&
+               git fetch --all &&
+               git branch branch --track origin/branch &&
+               git rebase origin/master
+       ) &&
+       (
+               cd dup &&
+               git switch master &&
+               test_commit F &&
+               test_commit G &&
+               git switch branch &&
+               test_commit H &&
+               git push origin --all
+       )
+}
+
 test_expect_success setup '
        # create template repository
        test_commit A &&
@@ -256,4 +296,101 @@ test_expect_success 'background updates of REMOTE can be mitigated with a non-up
        )
 '
 
+test_expect_success 'background updates to remote can be mitigated with "--force-if-includes"' '
+       setup_src_dup_dst &&
+       test_when_finished "rm -fr dst src dup" &&
+       git ls-remote dst refs/heads/master >expect.master &&
+       git ls-remote dst refs/heads/branch >expect.branch &&
+       (
+               cd src &&
+               git switch branch &&
+               test_commit I &&
+               git switch master &&
+               test_commit J &&
+               git fetch --all &&
+               test_must_fail git push --force-with-lease --force-if-includes --all
+       ) &&
+       git ls-remote dst refs/heads/master >actual.master &&
+       git ls-remote dst refs/heads/branch >actual.branch &&
+       test_cmp expect.master actual.master &&
+       test_cmp expect.branch actual.branch
+'
+
+test_expect_success 'background updates to remote can be mitigated with "push.useForceIfIncludes"' '
+       setup_src_dup_dst &&
+       test_when_finished "rm -fr dst src dup" &&
+       git ls-remote dst refs/heads/master >expect.master &&
+       (
+               cd src &&
+               git switch branch &&
+               test_commit I &&
+               git switch master &&
+               test_commit J &&
+               git fetch --all &&
+               git config --local push.useForceIfIncludes true &&
+               test_must_fail git push --force-with-lease=master origin master
+       ) &&
+       git ls-remote dst refs/heads/master >actual.master &&
+       test_cmp expect.master actual.master
+'
+
+test_expect_success '"--force-if-includes" should be disabled for --force-with-lease="<refname>:<expect>"' '
+       setup_src_dup_dst &&
+       test_when_finished "rm -fr dst src dup" &&
+       git ls-remote dst refs/heads/master >expect.master &&
+       (
+               cd src &&
+               git switch branch &&
+               test_commit I &&
+               git switch master &&
+               test_commit J &&
+               remote_head="$(git rev-parse refs/remotes/origin/master)" &&
+               git fetch --all &&
+               test_must_fail git push --force-if-includes --force-with-lease="master:$remote_head" 2>err &&
+               grep "stale info" err
+       ) &&
+       git ls-remote dst refs/heads/master >actual.master &&
+       test_cmp expect.master actual.master
+'
+
+test_expect_success '"--force-if-includes" should allow forced update after a rebase ("pull --rebase")' '
+       setup_src_dup_dst &&
+       test_when_finished "rm -fr dst src dup" &&
+       (
+               cd src &&
+               git switch branch &&
+               test_commit I &&
+               git switch master &&
+               test_commit J &&
+               git pull --rebase origin master &&
+               git push --force-if-includes --force-with-lease="master"
+       )
+'
+
+test_expect_success '"--force-if-includes" should allow forced update after a rebase ("pull --rebase", local rebase)' '
+       setup_src_dup_dst &&
+       test_when_finished "rm -fr dst src dup" &&
+       (
+               cd src &&
+               git switch branch &&
+               test_commit I &&
+               git switch master &&
+               test_commit J &&
+               git pull --rebase origin master &&
+               git rebase --onto HEAD~4 HEAD~1 &&
+               git push --force-if-includes --force-with-lease="master"
+       )
+'
+
+test_expect_success '"--force-if-includes" should allow deletes' '
+       setup_src_dup_dst &&
+       test_when_finished "rm -fr dst src dup" &&
+       (
+               cd src &&
+               git switch branch &&
+               git pull --rebase origin branch &&
+               git push --force-if-includes --force-with-lease="branch" origin :branch
+       )
+'
+
 test_done
index e69427f8817a6e2170ce1175991e8badb0e78ecb..7f082fb23b6af9292b279f9db129b8eb23a6e94b 100755 (executable)
@@ -15,7 +15,73 @@ test_expect_success 'setup' '
 test_expect_success 'clone -o' '
 
        git clone -o foo parent clone-o &&
-       (cd clone-o && git rev-parse --verify refs/remotes/foo/master)
+       git -C clone-o rev-parse --verify refs/remotes/foo/master
+
+'
+
+test_expect_success 'rejects invalid -o/--origin' '
+
+       test_must_fail git clone -o "bad...name" parent clone-bad-name 2>err &&
+       test_i18ngrep "'\''bad...name'\'' is not a valid remote name" err
+
+'
+
+test_expect_success 'disallows --bare with --origin' '
+
+       test_must_fail git clone -o foo --bare parent clone-bare-o 2>err &&
+       test_debug "cat err" &&
+       test_i18ngrep -e "--bare and --origin foo options are incompatible" err
+
+'
+
+test_expect_success 'disallows --bare with --separate-git-dir' '
+
+       test_must_fail git clone --bare --separate-git-dir dot-git-destiation parent clone-bare-sgd 2>err &&
+       test_debug "cat err" &&
+       test_i18ngrep -e "--bare and --separate-git-dir are incompatible" err
+
+'
+
+test_expect_success 'uses "origin" for default remote name' '
+
+       git clone parent clone-default-origin &&
+       git -C clone-default-origin rev-parse --verify refs/remotes/origin/master
+
+'
+
+test_expect_success 'prefers --template config over normal config' '
+
+       template="$TRASH_DIRECTORY/template-with-config" &&
+       mkdir "$template" &&
+       git config --file "$template/config" foo.bar from_template &&
+       test_config_global foo.bar from_global &&
+       git clone "--template=$template" parent clone-template-config &&
+       test "$(git -C clone-template-config config --local foo.bar)" = "from_template"
+
+'
+
+test_expect_success 'prefers -c config over --template config' '
+
+       template="$TRASH_DIRECTORY/template-with-ignored-config" &&
+       mkdir "$template" &&
+       git config --file "$template/config" foo.bar from_template &&
+       git clone "--template=$template" -c foo.bar=inline parent clone-template-inline-config &&
+       test "$(git -C clone-template-inline-config config --local foo.bar)" = "inline"
+
+'
+
+test_expect_success 'prefers config "clone.defaultRemoteName" over default' '
+
+       test_config_global clone.defaultRemoteName from_config &&
+       git clone parent clone-config-origin &&
+       git -C clone-config-origin rev-parse --verify refs/remotes/from_config/master
+
+'
+
+test_expect_success 'prefers --origin over -c config' '
+
+       git clone -c clone.defaultRemoteName=inline --origin from_option parent clone-o-and-inline-config &&
+       git -C clone-o-and-inline-config rev-parse --verify refs/remotes/from_option/master
 
 '
 
@@ -37,6 +103,7 @@ test_expect_success 'redirected clone -v does show progress' '
 
 test_expect_success 'chooses correct default initial branch name' '
        git init --bare empty &&
+       GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
        git -c init.defaultBranch=up clone empty whats-up &&
        test refs/heads/up = $(git -C whats-up symbolic-ref HEAD) &&
        test refs/heads/up = $(git -C whats-up config branch.up.merge)
@@ -51,9 +118,11 @@ test_expect_success 'guesses initial branch name correctly' '
 
        git -c init.defaultBranch=none init --bare no-head &&
        git -C initial-branch push ../no-head guess abc &&
+       GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
        git clone no-head is-it2 &&
        test_must_fail git -C is-it2 symbolic-ref refs/remotes/origin/HEAD &&
        git -C no-head update-ref --no-deref HEAD refs/heads/guess &&
+       GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
        git -c init.defaultBranch=guess clone no-head is-it3 &&
        test refs/remotes/origin/guess = \
                $(git -C is-it3 symbolic-ref refs/remotes/origin/HEAD)
index d9ecf0f4a95e2ef2bb43ee7816ee83a5c23b0b03..b46940b725ba007102acc9a3046ba117beab56d4 100755 (executable)
@@ -383,14 +383,14 @@ test_expect_success 'server is initially behind - ref in want' '
        test_cmp expected actual
 '
 
-test_expect_success 'server loses a ref - ref in want' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'server loses a ref - ref in want' '
        git -C "$REPO" config uploadpack.allowRefInWant true &&
        rm -rf local &&
        cp -r "$LOCAL_PRISTINE" local &&
-       echo "s/master/raster/" >"$HTTPD_ROOT_PATH/one-time-perl" &&
+       echo "s/main/rain/" >"$HTTPD_ROOT_PATH/one-time-perl" &&
        test_must_fail git -C local fetch 2>err &&
 
-       test_i18ngrep "fatal: remote error: unknown ref refs/heads/raster" err
+       test_i18ngrep "fatal: remote error: unknown ref refs/heads/rain" err
 '
 
 # DO NOT add non-httpd-specific tests here, because the last part of this
index bc95da8a5f72c1b9a9e523f82ce4404f92679d5e..99a1eaf332db7120cd0e957a9316c30cd5c01594 100755 (executable)
@@ -339,7 +339,7 @@ commit $head1
 .. (hinzugef${added_utf8_part_iso88591}gt) foo
 EOF
 
-test_expect_success 'prepare expected messages (for test %b)' '
+test_expect_success 'setup expected messages (for test %b)' '
        cat <<-EOF >expected.utf-8 &&
        commit $head3
        This commit message is much longer than the others,
index 7fc10f85930c70e0527efc068dfb94658ca172c8..fd202fcb9433451c5f0df412114eace11259e3f4 100755 (executable)
@@ -168,7 +168,7 @@ test_expect_success '--full-diff is not affected by --parents' '
 #
 # This example is explained in Documentation/rev-list-options.txt
 
-test_expect_success 'rebuild repo' '
+test_expect_success 'setup rebuild repo' '
        rm -rf .git * &&
        git init &&
        git switch -c topic &&
index 7d549748ef3015aade772944603e7b8efe20137b..f3e66eaf9bfe55cfa72a97461b23965013aa3d92 100755 (executable)
@@ -556,7 +556,7 @@ test_expect_success 'merge.suppressDest configuration' '
        head -n1 full.2 >actual &&
        grep -e "Merge branch .side. into master$" actual &&
 
-       git -c merge.suppressDest="ma??er" fmt-merge-msg <.git/FETCH_HEAD >full.3 &&
+       git -c merge.suppressDest="ma?*[rn]" fmt-merge-msg <.git/FETCH_HEAD >full.3 &&
        head -n1 full.3 >actual &&
        grep -e "Merge branch .side." actual &&
        ! grep -e " into master$" actual
index 781e470aeafb32a0c79abc40a12e1f55175aaba2..0a21669f56d5706be75914f8e2ccaa754caf3040 100755 (executable)
@@ -113,9 +113,9 @@ test_expect_success '%(color) must fail' '
        test_must_fail git for-each-ref --format="%(color)%(refname)"
 '
 
-test_expect_success 'left alignment is default' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'left alignment is default' '
        cat >expect <<-\EOF &&
-       refname is refs/heads/master  |refs/heads/master
+       refname is refs/heads/main    |refs/heads/main
        refname is refs/heads/side    |refs/heads/side
        refname is refs/odd/spot      |refs/odd/spot
        refname is refs/tags/annotated-tag|refs/tags/annotated-tag
@@ -131,9 +131,9 @@ test_expect_success 'left alignment is default' '
        test_cmp expect actual
 '
 
-test_expect_success 'middle alignment' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'middle alignment' '
        cat >expect <<-\EOF &&
-       | refname is refs/heads/master |refs/heads/master
+       |  refname is refs/heads/main  |refs/heads/main
        |  refname is refs/heads/side  |refs/heads/side
        |   refname is refs/odd/spot   |refs/odd/spot
        |refname is refs/tags/annotated-tag|refs/tags/annotated-tag
@@ -149,9 +149,9 @@ test_expect_success 'middle alignment' '
        test_cmp expect actual
 '
 
-test_expect_success 'right alignment' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'right alignment' '
        cat >expect <<-\EOF &&
-       |  refname is refs/heads/master|refs/heads/master
+       |    refname is refs/heads/main|refs/heads/main
        |    refname is refs/heads/side|refs/heads/side
        |      refname is refs/odd/spot|refs/odd/spot
        |refname is refs/tags/annotated-tag|refs/tags/annotated-tag
@@ -168,7 +168,7 @@ test_expect_success 'right alignment' '
 '
 
 cat >expect <<-\EOF
-|       refname is refs/heads/master       |refs/heads/master
+|        refname is refs/heads/main        |refs/heads/main
 |        refname is refs/heads/side        |refs/heads/side
 |         refname is refs/odd/spot         |refs/odd/spot
 |    refname is refs/tags/annotated-tag    |refs/tags/annotated-tag
@@ -184,7 +184,7 @@ EOF
 test_align_permutations() {
        while read -r option
        do
-               test_expect_success "align:$option" '
+               test_expect_success PREPARE_FOR_MAIN_BRANCH "align:$option" '
                        git for-each-ref --format="|%(align:$option)refname is %(refname)%(end)|%(refname)" >actual &&
                        test_cmp expect actual
                '
@@ -213,9 +213,9 @@ EOF
 
 # Individual atoms inside %(align:...) and %(end) must not be quoted.
 
-test_expect_success 'alignment with format quote' "
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'alignment with format quote' "
        cat >expect <<-\EOF &&
-       |'      '\''master| A U Thor'\''      '|
+       |'       '\''main| A U Thor'\''       '|
        |'       '\''side| A U Thor'\''       '|
        |'     '\''odd/spot| A U Thor'\''     '|
        |'      '\''annotated-tag| '\''       '|
@@ -231,9 +231,9 @@ test_expect_success 'alignment with format quote' "
        test_cmp expect actual
 "
 
-test_expect_success 'nested alignment with quote formatting' "
+test_expect_success PREPARE_FOR_MAIN_BRANCH 'nested alignment with quote formatting' "
        cat >expect <<-\EOF &&
-       |'         master               '|
+       |'           main               '|
        |'           side               '|
        |'       odd/spot               '|
        |'  annotated-tag               '|
index f7ecbb886d6191c4bb2e8c1da8b23e0a1e22d47d..06b46af765c25b94d1753ddeca592c700914b648 100755 (executable)
@@ -1277,20 +1277,114 @@ test_expect_success '6a: Tricky rename/delete' '
        )
 '
 
-# Testcase 6b, Same rename done on both sides
+# Testcase 6b1, Same rename done on both sides
+#   (Related to testcase 6b2 and 8e)
+#   Commit O: z/{b,c,d,e}
+#   Commit A: y/{b,c,d}, x/e
+#   Commit B: y/{b,c,d}, z/{e,f}
+#   Expected: y/{b,c,d,f}, x/e
+#   Note: Directory rename detection says A renamed z/ -> y/ (3 paths renamed
+#         to y/ and only 1 renamed to x/), therefore the new file 'z/f' in B
+#         should be moved to 'y/f'.
+#
+#         This is a bit of an edge case where any behavior might surprise users,
+#         whether that is treating A as renaming z/ -> y/, treating A as renaming
+#         z/ -> x/, or treating A as not doing any directory rename.  However, I
+#         think this answer is the least confusing and most consistent with the
+#         rules elsewhere.
+#
+#         A note about z/ -> x/, since it may not be clear how that could come
+#         about: If we were to ignore files renamed by both sides
+#         (i.e. z/{b,c,d}), as directory rename detection did in git-2.18 thru
+#         at least git-2.28, then we would note there are no renames from z/ to
+#         y/ and one rename from z/ to x/ and thus come to the conclusion that
+#         A renamed z/ -> x/.  This seems more confusing for end users than a
+#         rename of z/ to y/, it makes directory rename detection behavior
+#         harder for them to predict.  As such, we modified the rule, changed
+#         the behavior on testcases 6b2 and 8e, and introduced this 6b1 testcase.
+
+test_setup_6b1 () {
+       test_create_repo 6b1 &&
+       (
+               cd 6b1 &&
+
+               mkdir z &&
+               echo b >z/b &&
+               echo c >z/c &&
+               echo d >z/d &&
+               echo e >z/e &&
+               git add z &&
+               test_tick &&
+               git commit -m "O" &&
+
+               git branch O &&
+               git branch A &&
+               git branch B &&
+
+               git checkout A &&
+               git mv z y &&
+               mkdir x &&
+               git mv y/e x/e &&
+               test_tick &&
+               git commit -m "A" &&
+
+               git checkout B &&
+               git mv z y &&
+               mkdir z &&
+               git mv y/e z/e &&
+               echo f >z/f &&
+               git add z/f &&
+               test_tick &&
+               git commit -m "B"
+       )
+}
+
+test_expect_failure '6b1: Same renames done on both sides, plus another rename' '
+       test_setup_6b1 &&
+       (
+               cd 6b1 &&
+
+               git checkout A^0 &&
+
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 5 out &&
+               git ls-files -u >out &&
+               test_line_count = 0 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               git rev-parse >actual \
+                       HEAD:y/b HEAD:y/c HEAD:y/d HEAD:x/e HEAD:y/f &&
+               git rev-parse >expect \
+                       O:z/b    O:z/c    O:z/d    O:z/e    B:z/f &&
+               test_cmp expect actual
+       )
+'
+
+# Testcase 6b2, Same rename done on both sides
 #   (Related to testcases 6c and 8e)
 #   Commit O: z/{b,c}
 #   Commit A: y/{b,c}
 #   Commit B: y/{b,c}, z/d
-#   Expected: y/{b,c}, z/d
-#   Note: If we did directory rename detection here, we'd move z/d into y/,
-#         but B did that rename and still decided to put the file into z/,
-#         so we probably shouldn't apply directory rename detection for it.
-
-test_setup_6b () {
-       test_create_repo 6b &&
+#   Expected: y/{b,c,d}
+#   Alternate: y/{b,c}, z/d
+#   Note: Directory rename detection says A renamed z/ -> y/, therefore the new
+#         file 'z/d' in B should be moved to 'y/d'.
+#
+#         We could potentially ignore the renames of z/{b,c} on side A since
+#         those were renamed on both sides.  However, it's a bit of a corner
+#         case because what if there was also a z/e that side A moved to x/e
+#         and side B left alone?  If we used the "ignore renames done on both
+#         sides" logic, then we'd compute that A renamed z/ -> x/, and move
+#         z/d to x/d.  That seems more surprising and uglier than allowing
+#         the z/ -> y/ rename.
+
+test_setup_6b2 () {
+       test_create_repo 6b2 &&
        (
-               cd 6b &&
+               cd 6b2 &&
 
                mkdir z &&
                echo b >z/b &&
@@ -1318,10 +1412,10 @@ test_setup_6b () {
        )
 }
 
-test_expect_success '6b: Same rename done on both sides' '
-       test_setup_6b &&
+test_expect_failure '6b2: Same rename done on both sides' '
+       test_setup_6b2 &&
        (
-               cd 6b &&
+               cd 6b2 &&
 
                git checkout A^0 &&
 
@@ -1335,7 +1429,7 @@ test_expect_success '6b: Same rename done on both sides' '
                test_line_count = 1 out &&
 
                git rev-parse >actual \
-                       HEAD:y/b HEAD:y/c HEAD:z/d &&
+                       HEAD:y/b HEAD:y/c HEAD:y/d &&
                git rev-parse >expect \
                        O:z/b    O:z/c    B:z/d &&
                test_cmp expect actual
@@ -1343,7 +1437,7 @@ test_expect_success '6b: Same rename done on both sides' '
 '
 
 # Testcase 6c, Rename only done on same side
-#   (Related to testcases 6b and 8e)
+#   (Related to testcases 6b1, 6b2, and 8e)
 #   Commit O: z/{b,c}
 #   Commit A: z/{b,c} (no change)
 #   Commit B: y/{b,c}, z/d
@@ -2269,14 +2363,22 @@ test_expect_success '8d: rename/delete...or not?' '
 # Notes: In commit A, directory z got renamed to y.  In commit B, directory z
 #        did NOT get renamed; the directory is still present; instead it is
 #        considered to have just renamed a subset of paths in directory z
-#        elsewhere.  However, this is much like testcase 6b (where commit B
-#        moves all the original paths out of z/ but opted to keep d
-#        within z/).  This makes it hard to judge where d should end up.
+#        elsewhere.  This is much like testcase 6b2 (where commit B moves all
+#        the original paths out of z/ but opted to keep d within z/).
 #
-#        It's possible that users would get confused about this, but what
-#        should we do instead?  It's not at all clear to me whether z/d or
-#        y/d or something else is a better resolution here, and other cases
-#        start getting really tricky, so I just picked one.
+#        It was not clear in the past what should be done with this testcase;
+#        in fact, I noted that I "just picked one" previously.  However,
+#        following the new logic for testcase 6b2, we should take the rename
+#        and move z/d to y/d.
+#
+#        6b1, 6b2, and this case are definitely somewhat fuzzy in terms of
+#        whether they are optimal for end users, but (a) the default for
+#        directory rename detection is to mark these all as conflicts
+#        anyway, (b) it feels like this is less prone to higher order corner
+#        case confusion, and (c) the current algorithm requires less global
+#        knowledge (i.e. less coupling in the algorithm between renames done
+#        on both sides) which thus means users are better able to predict
+#        the behavior, and predict it without computing as many details.
 
 test_setup_8e () {
        test_create_repo 8e &&
@@ -3947,31 +4049,124 @@ test_expect_success '12a: Moving one directory hierarchy into another' '
        )
 '
 
-# Testcase 12b, Moving two directory hierarchies into each other
+# Testcase 12b1, Moving two directory hierarchies into each other
 #   (Related to testcases 1c and 12c)
 #   Commit O: node1/{leaf1, leaf2}, node2/{leaf3, leaf4}
 #   Commit A: node1/{leaf1, leaf2, node2/{leaf3, leaf4}}
 #   Commit B: node2/{leaf3, leaf4, node1/{leaf1, leaf2}}
-#   Expected: node1/node2/node1/{leaf1, leaf2},
+#   Expected: node1/node2/{leaf3, leaf4}
+#             node2/node1/{leaf1, leaf2}
+#   NOTE: If there were new files added to the old node1/ or node2/ directories,
+#         then we would need to detect renames for those directories and would
+#         find that:
+#             commit A renames node2/ -> node1/node2/
+#             commit B renames node1/ -> node2/node1/
+#         Applying those directory renames to the initial result (making all
+#         four paths experience a transitive renaming), yields
+#             node1/node2/node1/{leaf1, leaf2}
 #             node2/node1/node2/{leaf3, leaf4}
+#         as the result.  It may be really weird to have two directories
+#         rename each other, but simple rules give weird results when given
+#         weird inputs.  HOWEVER, the "If" at the beginning of those NOTE was
+#         false; there were no new files added and thus there is no directory
+#         rename detection to perform.  As such, we just have simple renames
+#         and the expected answer is:
+#             node1/node2/{leaf3, leaf4}
+#             node2/node1/{leaf1, leaf2}
+
+test_setup_12b1 () {
+       test_create_repo 12b1 &&
+       (
+               cd 12b1 &&
+
+               mkdir -p node1 node2 &&
+               echo leaf1 >node1/leaf1 &&
+               echo leaf2 >node1/leaf2 &&
+               echo leaf3 >node2/leaf3 &&
+               echo leaf4 >node2/leaf4 &&
+               git add node1 node2 &&
+               test_tick &&
+               git commit -m "O" &&
+
+               git branch O &&
+               git branch A &&
+               git branch B &&
+
+               git checkout A &&
+               git mv node2/ node1/ &&
+               test_tick &&
+               git commit -m "A" &&
+
+               git checkout B &&
+               git mv node1/ node2/ &&
+               test_tick &&
+               git commit -m "B"
+       )
+}
+
+test_expect_failure '12b1: Moving two directory hierarchies into each other' '
+       test_setup_12b1 &&
+       (
+               cd 12b1 &&
+
+               git checkout A^0 &&
+
+               git -c merge.directoryRenames=true merge -s recursive B^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 4 out &&
+
+               git rev-parse >actual \
+                       HEAD:node2/node1/leaf1 \
+                       HEAD:node2/node1/leaf2 \
+                       HEAD:node1/node2/leaf3 \
+                       HEAD:node1/node2/leaf4 &&
+               git rev-parse >expect \
+                       O:node1/leaf1 \
+                       O:node1/leaf2 \
+                       O:node2/leaf3 \
+                       O:node2/leaf4 &&
+               test_cmp expect actual
+       )
+'
+
+# Testcase 12b2, Moving two directory hierarchies into each other
+#   (Related to testcases 1c and 12c)
+#   Commit O: node1/{leaf1, leaf2}, node2/{leaf3, leaf4}
+#   Commit A: node1/{leaf1, leaf2, leaf5, node2/{leaf3, leaf4}}
+#   Commit B: node2/{leaf3, leaf4, leaf6, node1/{leaf1, leaf2}}
+#   Expected: node1/node2/{node1/{leaf1, leaf2}, leaf6}
+#             node2/node1/{node2/{leaf3, leaf4}, leaf5}
 #   NOTE: Without directory renames, we would expect
-#                   node2/node1/{leaf1, leaf2},
-#                   node1/node2/{leaf3, leaf4}
+#             A: node2/leaf3 -> node1/node2/leaf3
+#             A: node2/leaf1 -> node1/node2/leaf4
+#             A: Adds           node1/leaf5
+#             B: node1/leaf1 -> node2/node1/leaf1
+#             B: node1/leaf2 -> node2/node1/leaf2
+#             B: Adds           node2/leaf6
 #         with directory rename detection, we note that
 #             commit A renames node2/ -> node1/node2/
 #             commit B renames node1/ -> node2/node1/
-#         therefore, applying those directory renames to the initial result
-#         (making all four paths experience a transitive renaming), yields
-#         the expected result.
+#         therefore, applying A's directory rename to the paths added in B gives:
+#             B: node1/leaf1 -> node1/node2/node1/leaf1
+#             B: node1/leaf2 -> node1/node2/node1/leaf2
+#             B: Adds           node1/node2/leaf6
+#         and applying B's directory rename to the paths added in A gives:
+#             A: node2/leaf3 -> node2/node1/node2/leaf3
+#             A: node2/leaf1 -> node2/node1/node2/leaf4
+#             A: Adds           node2/node1/leaf5
+#         resulting in the expected
+#             node1/node2/{node1/{leaf1, leaf2}, leaf6}
+#             node2/node1/{node2/{leaf3, leaf4}, leaf5}
 #
 #         You may ask, is it weird to have two directories rename each other?
 #         To which, I can do no more than shrug my shoulders and say that
 #         even simple rules give weird results when given weird inputs.
 
-test_setup_12b () {
-       test_create_repo 12b &&
+test_setup_12b2 () {
+       test_create_repo 12b2 &&
        (
-               cd 12b &&
+               cd 12b2 &&
 
                mkdir -p node1 node2 &&
                echo leaf1 >node1/leaf1 &&
@@ -3988,43 +4183,51 @@ test_setup_12b () {
 
                git checkout A &&
                git mv node2/ node1/ &&
+               echo leaf5 >node1/leaf5 &&
+               git add node1/leaf5 &&
                test_tick &&
                git commit -m "A" &&
 
                git checkout B &&
                git mv node1/ node2/ &&
+               echo leaf6 >node2/leaf6 &&
+               git add node2/leaf6 &&
                test_tick &&
                git commit -m "B"
        )
 }
 
-test_expect_success '12b: Moving two directory hierarchies into each other' '
-       test_setup_12b &&
+test_expect_success '12b2: Moving two directory hierarchies into each other' '
+       test_setup_12b2 &&
        (
-               cd 12b &&
+               cd 12b2 &&
 
                git checkout A^0 &&
 
                git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
                git ls-files -s >out &&
-               test_line_count = 4 out &&
+               test_line_count = 6 out &&
 
                git rev-parse >actual \
                        HEAD:node1/node2/node1/leaf1 \
                        HEAD:node1/node2/node1/leaf2 \
                        HEAD:node2/node1/node2/leaf3 \
-                       HEAD:node2/node1/node2/leaf4 &&
+                       HEAD:node2/node1/node2/leaf4 \
+                       HEAD:node2/node1/leaf5       \
+                       HEAD:node1/node2/leaf6       &&
                git rev-parse >expect \
                        O:node1/leaf1 \
                        O:node1/leaf2 \
                        O:node2/leaf3 \
-                       O:node2/leaf4 &&
+                       O:node2/leaf4 \
+                       A:node1/leaf5 \
+                       B:node2/leaf6 &&
                test_cmp expect actual
        )
 '
 
-# Testcase 12c, Moving two directory hierarchies into each other w/ content merge
+# Testcase 12c1, Moving two directory hierarchies into each other w/ content merge
 #   (Related to testcase 12b)
 #   Commit O: node1/{       leaf1_1, leaf2_1}, node2/{leaf3_1, leaf4_1}
 #   Commit A: node1/{       leaf1_2, leaf2_2,  node2/{leaf3_2, leaf4_2}}
@@ -4032,13 +4235,103 @@ test_expect_success '12b: Moving two directory hierarchies into each other' '
 #   Expected: Content merge conflicts for each of:
 #               node1/node2/node1/{leaf1, leaf2},
 #               node2/node1/node2/{leaf3, leaf4}
-#   NOTE: This is *exactly* like 12c, except that every path is modified on
+#   NOTE: This is *exactly* like 12b1, except that every path is modified on
 #         each side of the merge.
 
-test_setup_12c () {
-       test_create_repo 12c &&
+test_setup_12c1 () {
+       test_create_repo 12c1 &&
+       (
+               cd 12c1 &&
+
+               mkdir -p node1 node2 &&
+               printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf1\n" >node1/leaf1 &&
+               printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf2\n" >node1/leaf2 &&
+               printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf3\n" >node2/leaf3 &&
+               printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf4\n" >node2/leaf4 &&
+               git add node1 node2 &&
+               test_tick &&
+               git commit -m "O" &&
+
+               git branch O &&
+               git branch A &&
+               git branch B &&
+
+               git checkout A &&
+               git mv node2/ node1/ &&
+               for i in `git ls-files`; do echo side A >>$i; done &&
+               git add -u &&
+               test_tick &&
+               git commit -m "A" &&
+
+               git checkout B &&
+               git mv node1/ node2/ &&
+               for i in `git ls-files`; do echo side B >>$i; done &&
+               git add -u &&
+               test_tick &&
+               git commit -m "B"
+       )
+}
+
+test_expect_failure '12c1: Moving one directory hierarchy into another w/ content merge' '
+       test_setup_12c1 &&
+       (
+               cd 12c1 &&
+
+               git checkout A^0 &&
+
+               test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 &&
+
+               git ls-files -u >out &&
+               test_line_count = 12 out &&
+
+               git rev-parse >actual \
+                       :1:node2/node1/leaf1 \
+                       :1:node2/node1/leaf2 \
+                       :1:node1/node2/leaf3 \
+                       :1:node1/node2/leaf4 \
+                       :2:node2/node1/leaf1 \
+                       :2:node2/node1/leaf2 \
+                       :2:node1/node2/leaf3 \
+                       :2:node1/node2/leaf4 \
+                       :3:node2/node1/leaf1 \
+                       :3:node2/node1/leaf2 \
+                       :3:node1/node2/leaf3 \
+                       :3:node1/node2/leaf4 &&
+               git rev-parse >expect \
+                       O:node1/leaf1 \
+                       O:node1/leaf2 \
+                       O:node2/leaf3 \
+                       O:node2/leaf4 \
+                       A:node1/leaf1 \
+                       A:node1/leaf2 \
+                       A:node1/node2/leaf3 \
+                       A:node1/node2/leaf4 \
+                       B:node2/node1/leaf1 \
+                       B:node2/node1/leaf2 \
+                       B:node2/leaf3 \
+                       B:node2/leaf4 &&
+               test_cmp expect actual
+       )
+'
+
+# Testcase 12c2, Moving two directory hierarchies into each other w/ content merge
+#   (Related to testcase 12b)
+#   Commit O: node1/{       leaf1_1, leaf2_1}, node2/{leaf3_1, leaf4_1}
+#   Commit A: node1/{       leaf1_2, leaf2_2,  node2/{leaf3_2, leaf4_2}, leaf5}
+#   Commit B: node2/{node1/{leaf1_3, leaf2_3},        leaf3_3, leaf4_3,  leaf6}
+#   Expected: Content merge conflicts for each of:
+#               node1/node2/node1/{leaf1, leaf2}
+#               node2/node1/node2/{leaf3, leaf4}
+#             plus
+#               node2/node1/leaf5
+#               node1/node2/leaf6
+#   NOTE: This is *exactly* like 12b2, except that every path from O is modified
+#         on each side of the merge.
+
+test_setup_12c2 () {
+       test_create_repo 12c2 &&
        (
-               cd 12c &&
+               cd 12c2 &&
 
                mkdir -p node1 node2 &&
                printf "1\n2\n3\n4\n5\n6\n7\n8\nleaf1\n" >node1/leaf1 &&
@@ -4057,6 +4350,8 @@ test_setup_12c () {
                git mv node2/ node1/ &&
                for i in `git ls-files`; do echo side A >>$i; done &&
                git add -u &&
+               echo leaf5 >node1/leaf5 &&
+               git add node1/leaf5 &&
                test_tick &&
                git commit -m "A" &&
 
@@ -4064,20 +4359,24 @@ test_setup_12c () {
                git mv node1/ node2/ &&
                for i in `git ls-files`; do echo side B >>$i; done &&
                git add -u &&
+               echo leaf6 >node2/leaf6 &&
+               git add node2/leaf6 &&
                test_tick &&
                git commit -m "B"
        )
 }
 
-test_expect_success '12c: Moving one directory hierarchy into another w/ content merge' '
-       test_setup_12c &&
+test_expect_success '12c2: Moving one directory hierarchy into another w/ content merge' '
+       test_setup_12c2 &&
        (
-               cd 12c &&
+               cd 12c2 &&
 
                git checkout A^0 &&
 
                test_must_fail git -c merge.directoryRenames=true merge -s recursive B^0 &&
 
+               git ls-files -s >out &&
+               test_line_count = 14 out &&
                git ls-files -u >out &&
                test_line_count = 12 out &&
 
@@ -4093,7 +4392,9 @@ test_expect_success '12c: Moving one directory hierarchy into another w/ content
                        :3:node1/node2/node1/leaf1 \
                        :3:node1/node2/node1/leaf2 \
                        :3:node2/node1/node2/leaf3 \
-                       :3:node2/node1/node2/leaf4 &&
+                       :3:node2/node1/node2/leaf4 \
+                       :0:node2/node1/leaf5       \
+                       :0:node1/node2/leaf6       &&
                git rev-parse >expect \
                        O:node1/leaf1 \
                        O:node1/leaf2 \
@@ -4106,7 +4407,9 @@ test_expect_success '12c: Moving one directory hierarchy into another w/ content
                        B:node2/node1/leaf1 \
                        B:node2/node1/leaf2 \
                        B:node2/leaf3 \
-                       B:node2/leaf4 &&
+                       B:node2/leaf4 \
+                       A:node1/leaf5 \
+                       B:node2/leaf6 &&
                test_cmp expect actual
        )
 '
@@ -4227,6 +4530,201 @@ test_expect_success '12e: Rename/merge subdir into the root, variant 2' '
        )
 '
 
+# Testcase 12f, Rebase of patches with big directory rename
+#   Commit O:
+#              dir/subdir/{a,b,c,d,e_O,Makefile_TOP_O}
+#              dir/subdir/tweaked/{f,g,h,Makefile_SUB_O}
+#              dir/unchanged/<LOTS OF FILES>
+#   Commit A:
+#     (Remove f & g, move e into newsubdir, rename dir/->folder/, modify files)
+#              folder/subdir/{a,b,c,d,Makefile_TOP_A}
+#              folder/subdir/newsubdir/e_A
+#              folder/subdir/tweaked/{h,Makefile_SUB_A}
+#              folder/unchanged/<LOTS OF FILES>
+#   Commit B1:
+#     (add newfile.{c,py}, modify underscored files)
+#              dir/{a,b,c,d,e_B1,Makefile_TOP_B1,newfile.c}
+#              dir/tweaked/{f,g,h,Makefile_SUB_B1,newfile.py}
+#              dir/unchanged/<LOTS OF FILES>
+#   Commit B2:
+#     (Modify e further, add newfile.rs)
+#              dir/{a,b,c,d,e_B2,Makefile_TOP_B1,newfile.c,newfile.rs}
+#              dir/tweaked/{f,g,h,Makefile_SUB_B1,newfile.py}
+#              dir/unchanged/<LOTS OF FILES>
+#   Expected:
+#          B1-picked:
+#              folder/subdir/{a,b,c,d,Makefile_TOP_Merge1,newfile.c}
+#              folder/subdir/newsubdir/e_Merge1
+#              folder/subdir/tweaked/{h,Makefile_SUB_Merge1,newfile.py}
+#              folder/unchanged/<LOTS OF FILES>
+#          B2-picked:
+#              folder/subdir/{a,b,c,d,Makefile_TOP_Merge1,newfile.c,newfile.rs}
+#              folder/subdir/newsubdir/e_Merge2
+#              folder/subdir/tweaked/{h,Makefile_SUB_Merge1,newfile.py}
+#              folder/unchanged/<LOTS OF FILES>
+#
+# Notes: This testcase happens to exercise lots of the
+#        optimization-specific codepaths in merge-ort, and also
+#        demonstrated a failing of the directory rename detection algorithm
+#        in merge-recursive; newfile.c should not get pushed into
+#        folder/subdir/newsubdir/, yet merge-recursive put it there because
+#        the rename of dir/subdir/{a,b,c,d} -> folder/subdir/{a,b,c,d}
+#        looks like
+#            dir/ -> folder/,
+#        whereas the rename of dir/subdir/e -> folder/subdir/newsubdir/e
+#        looks like
+#            dir/subdir/ -> folder/subdir/newsubdir/
+#        and if we note that newfile.c is found in dir/subdir/, we might
+#        overlook the dir/ -> folder/ rule that has more weight.
+
+test_setup_12f () {
+       test_create_repo 12f &&
+       (
+               cd 12f &&
+
+               mkdir -p dir/unchanged &&
+               mkdir -p dir/subdir/tweaked &&
+               echo a >dir/subdir/a &&
+               echo b >dir/subdir/b &&
+               echo c >dir/subdir/c &&
+               echo d >dir/subdir/d &&
+               test_seq 1 10 >dir/subdir/e &&
+               test_seq 10 20 >dir/subdir/Makefile &&
+               echo f >dir/subdir/tweaked/f &&
+               echo g >dir/subdir/tweaked/g &&
+               echo h >dir/subdir/tweaked/h &&
+               test_seq 20 30 >dir/subdir/tweaked/Makefile &&
+               for i in `test_seq 1 88`; do
+                       echo content $i >dir/unchanged/file_$i
+               done &&
+               git add . &&
+               git commit -m "O" &&
+
+               git branch O &&
+               git branch A &&
+               git branch B &&
+
+               git switch A &&
+               git rm dir/subdir/tweaked/f dir/subdir/tweaked/g &&
+               test_seq 2 10 >dir/subdir/e &&
+               test_seq 11 20 >dir/subdir/Makefile &&
+               test_seq 21 30 >dir/subdir/tweaked/Makefile &&
+               mkdir dir/subdir/newsubdir &&
+               git mv dir/subdir/e dir/subdir/newsubdir/ &&
+               git mv dir folder &&
+               git add . &&
+               git commit -m "A" &&
+
+               git switch B &&
+               mkdir dir/subdir/newsubdir/ &&
+               echo c code >dir/subdir/newfile.c &&
+               echo python code >dir/subdir/newsubdir/newfile.py &&
+               test_seq 1 11 >dir/subdir/e &&
+               test_seq 10 21 >dir/subdir/Makefile &&
+               test_seq 20 31 >dir/subdir/tweaked/Makefile &&
+               git add . &&
+               git commit -m "B1" &&
+
+               echo rust code >dir/subdir/newfile.rs &&
+               test_seq 1 12 >dir/subdir/e &&
+               git add . &&
+               git commit -m "B2"
+       )
+}
+
+test_expect_failure '12f: Trivial directory resolve, caching, all kinds of fun' '
+       test_setup_12f &&
+       (
+               cd 12f &&
+
+               git checkout A^0 &&
+               git branch Bmod B &&
+
+               git -c merge.directoryRenames=true rebase A Bmod &&
+
+               echo Checking the pick of B1... &&
+
+               test_must_fail git rev-parse Bmod~1:dir &&
+
+               git ls-tree -r Bmod~1 >out &&
+               test_line_count = 98 out &&
+
+               git diff --name-status A Bmod~1 >actual &&
+               q_to_tab >expect <<-\EOF &&
+               MQfolder/subdir/Makefile
+               AQfolder/subdir/newfile.c
+               MQfolder/subdir/newsubdir/e
+               AQfolder/subdir/newsubdir/newfile.py
+               MQfolder/subdir/tweaked/Makefile
+               EOF
+               test_cmp expect actual &&
+
+               # Three-way merged files
+               test_seq  2 11 >e_Merge1 &&
+               test_seq 11 21 >Makefile_TOP &&
+               test_seq 21 31 >Makefile_SUB &&
+               git hash-object >expect      \
+                       e_Merge1             \
+                       Makefile_TOP         \
+                       Makefile_SUB         &&
+               git rev-parse >actual              \
+                       Bmod~1:folder/subdir/newsubdir/e     \
+                       Bmod~1:folder/subdir/Makefile        \
+                       Bmod~1:folder/subdir/tweaked/Makefile &&
+               test_cmp expect actual &&
+
+               # New files showed up at the right location with right contents
+               git rev-parse >expect                \
+                       B~1:dir/subdir/newfile.c            \
+                       B~1:dir/subdir/newsubdir/newfile.py &&
+               git rev-parse >actual                      \
+                       Bmod~1:folder/subdir/newfile.c            \
+                       Bmod~1:folder/subdir/newsubdir/newfile.py &&
+               test_cmp expect actual &&
+
+               # Removed files
+               test_path_is_missing folder/subdir/tweaked/f &&
+               test_path_is_missing folder/subdir/tweaked/g &&
+
+               # Unchanged files or directories
+               git rev-parse >actual        \
+                       Bmod~1:folder/subdir/a          \
+                       Bmod~1:folder/subdir/b          \
+                       Bmod~1:folder/subdir/c          \
+                       Bmod~1:folder/subdir/d          \
+                       Bmod~1:folder/unchanged         \
+                       Bmod~1:folder/subdir/tweaked/h &&
+               git rev-parse >expect          \
+                       O:dir/subdir/a         \
+                       O:dir/subdir/b         \
+                       O:dir/subdir/c         \
+                       O:dir/subdir/d         \
+                       O:dir/unchanged        \
+                       O:dir/subdir/tweaked/h &&
+               test_cmp expect actual &&
+
+               echo Checking the pick of B2... &&
+
+               test_must_fail git rev-parse Bmod:dir &&
+
+               git ls-tree -r Bmod >out &&
+               test_line_count = 99 out &&
+
+               git diff --name-status Bmod~1 Bmod >actual &&
+               q_to_tab >expect <<-\EOF &&
+               AQfolder/subdir/newfile.rs
+               MQfolder/subdir/newsubdir/e
+               EOF
+               test_cmp expect actual &&
+
+               # Three-way merged file
+               test_seq  2 12 >e_Merge2 &&
+               git hash-object e_Merge2 >expect &&
+               git rev-parse Bmod:folder/subdir/newsubdir/e >actual &&
+               test_cmp expect actual
+       )
+'
+
 ###########################################################################
 # SECTION 13: Checking informational and conflict messages
 #
index 00e09a375c2e6e3bfce2c2f8ce93493513082b2d..fdb450e446a987717d4565ef58cccfa332259927 100755 (executable)
@@ -19,7 +19,7 @@ test_expect_success 'setup' '
 test_expect_success TTY 'some commands use a pager' '
        rm -f paginated.out &&
        test_terminal git log &&
-       test -e paginated.out
+       test_path_is_file paginated.out
 '
 
 test_expect_failure TTY 'pager runs from subdir' '
@@ -65,49 +65,49 @@ test_expect_success !MINGW,TTY 'LESS and LV envvars set by git-sh-setup' '
 test_expect_success TTY 'some commands do not use a pager' '
        rm -f paginated.out &&
        test_terminal git rev-list HEAD &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success 'no pager when stdout is a pipe' '
        rm -f paginated.out &&
        git log | cat &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success 'no pager when stdout is a regular file' '
        rm -f paginated.out &&
        git log >file &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git --paginate rev-list uses a pager' '
        rm -f paginated.out &&
        test_terminal git --paginate rev-list HEAD &&
-       test -e paginated.out
+       test_path_is_file paginated.out
 '
 
 test_expect_success 'no pager even with --paginate when stdout is a pipe' '
        rm -f file paginated.out &&
        git --paginate log | cat &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'no pager with --no-pager' '
        rm -f paginated.out &&
        test_terminal git --no-pager log &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'configuration can disable pager' '
        rm -f paginated.out &&
        test_unconfig pager.grep &&
        test_terminal git grep initial &&
-       test -e paginated.out &&
+       test_path_is_file paginated.out &&
 
        rm -f paginated.out &&
        test_config pager.grep false &&
        test_terminal git grep initial &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'configuration can enable pager (from subdir)' '
@@ -122,107 +122,107 @@ test_expect_success TTY 'configuration can enable pager (from subdir)' '
                test_terminal git bundle unbundle ../test.bundle
        ) &&
        {
-               test -e paginated.out ||
-               test -e subdir/paginated.out
+               test_path_is_file paginated.out ||
+               test_path_is_file subdir/paginated.out
        }
 '
 
 test_expect_success TTY 'git tag -l defaults to paging' '
        rm -f paginated.out &&
        test_terminal git tag -l &&
-       test -e paginated.out
+       test_path_is_file paginated.out
 '
 
 test_expect_success TTY 'git tag -l respects pager.tag' '
        rm -f paginated.out &&
        test_terminal git -c pager.tag=false tag -l &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git tag -l respects --no-pager' '
        rm -f paginated.out &&
        test_terminal git -c pager.tag --no-pager tag -l &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git tag with no args defaults to paging' '
        # no args implies -l so this should page like -l
        rm -f paginated.out &&
        test_terminal git tag &&
-       test -e paginated.out
+       test_path_is_file paginated.out
 '
 
 test_expect_success TTY 'git tag with no args respects pager.tag' '
        # no args implies -l so this should page like -l
        rm -f paginated.out &&
        test_terminal git -c pager.tag=false tag &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git tag --contains defaults to paging' '
        # --contains implies -l so this should page like -l
        rm -f paginated.out &&
        test_terminal git tag --contains &&
-       test -e paginated.out
+       test_path_is_file paginated.out
 '
 
 test_expect_success TTY 'git tag --contains respects pager.tag' '
        # --contains implies -l so this should page like -l
        rm -f paginated.out &&
        test_terminal git -c pager.tag=false tag --contains &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git tag -a defaults to not paging' '
        test_when_finished "git tag -d newtag" &&
        rm -f paginated.out &&
        test_terminal git tag -am message newtag &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git tag -a ignores pager.tag' '
        test_when_finished "git tag -d newtag" &&
        rm -f paginated.out &&
        test_terminal git -c pager.tag tag -am message newtag &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git tag -a respects --paginate' '
        test_when_finished "git tag -d newtag" &&
        rm -f paginated.out &&
        test_terminal git --paginate tag -am message newtag &&
-       test -e paginated.out
+       test_path_is_file paginated.out
 '
 
 test_expect_success TTY 'git tag as alias ignores pager.tag with -a' '
        test_when_finished "git tag -d newtag" &&
        rm -f paginated.out &&
        test_terminal git -c pager.tag -c alias.t=tag t -am message newtag &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git tag as alias respects pager.tag with -l' '
        rm -f paginated.out &&
        test_terminal git -c pager.tag=false -c alias.t=tag t -l &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git branch defaults to paging' '
        rm -f paginated.out &&
        test_terminal git branch &&
-       test -e paginated.out
+       test_path_is_file paginated.out
 '
 
 test_expect_success TTY 'git branch respects pager.branch' '
        rm -f paginated.out &&
        test_terminal git -c pager.branch=false branch &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git branch respects --no-pager' '
        rm -f paginated.out &&
        test_terminal git --no-pager branch &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git branch --edit-description ignores pager.branch' '
@@ -232,8 +232,8 @@ test_expect_success TTY 'git branch --edit-description ignores pager.branch' '
                touch editor.used
        EOF
        EDITOR=./editor test_terminal git -c pager.branch branch --edit-description &&
-       ! test -e paginated.out &&
-       test -e editor.used
+       test_path_is_missing paginated.out &&
+       test_path_is_file editor.used
 '
 
 test_expect_success TTY 'git branch --set-upstream-to ignores pager.branch' '
@@ -242,13 +242,13 @@ test_expect_success TTY 'git branch --set-upstream-to ignores pager.branch' '
        test_when_finished "git branch -D other" &&
        test_terminal git -c pager.branch branch --set-upstream-to=other &&
        test_when_finished "git branch --unset-upstream" &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git config ignores pager.config when setting' '
        rm -f paginated.out &&
        test_terminal git -c pager.config config foo.bar bar &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git config --edit ignores pager.config' '
@@ -257,33 +257,33 @@ test_expect_success TTY 'git config --edit ignores pager.config' '
                touch editor.used
        EOF
        EDITOR=./editor test_terminal git -c pager.config config --edit &&
-       ! test -e paginated.out &&
-       test -e editor.used
+       test_path_is_missing paginated.out &&
+       test_path_is_file editor.used
 '
 
 test_expect_success TTY 'git config --get ignores pager.config' '
        rm -f paginated.out &&
        test_terminal git -c pager.config config --get foo.bar &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git config --get-urlmatch defaults to paging' '
        rm -f paginated.out &&
        test_terminal git -c http."https://foo.com/".bar=foo \
                          config --get-urlmatch http https://foo.com &&
-       test -e paginated.out
+       test_path_is_file paginated.out
 '
 
 test_expect_success TTY 'git config --get-all respects pager.config' '
        rm -f paginated.out &&
        test_terminal git -c pager.config=false config --get-all foo.bar &&
-       ! test -e paginated.out
+       test_path_is_missing paginated.out
 '
 
 test_expect_success TTY 'git config --list defaults to paging' '
        rm -f paginated.out &&
        test_terminal git config --list &&
-       test -e paginated.out
+       test_path_is_file paginated.out
 '
 
 
@@ -392,7 +392,7 @@ test_default_pager() {
                        export PATH &&
                        $full_command
                ) &&
-               test -e default_pager_used
+               test_path_is_file default_pager_used
        "
 }
 
@@ -406,7 +406,7 @@ test_PAGER_overrides() {
                PAGER='wc >PAGER_used' &&
                export PAGER &&
                $full_command &&
-               test -e PAGER_used
+               test_path_is_file PAGER_used
        "
 }
 
@@ -432,7 +432,7 @@ test_core_pager() {
                export PAGER &&
                test_config core.pager 'wc >core.pager_used' &&
                $full_command &&
-               ${if_local_config}test -e core.pager_used
+               ${if_local_config}test_path_is_file core.pager_used
        "
 }
 
@@ -464,7 +464,7 @@ test_pager_subdir_helper() {
                        cd sub &&
                        $full_command
                ) &&
-               ${if_local_config}test -e core.pager_used
+               ${if_local_config}test_path_is_file core.pager_used
        "
 }
 
@@ -477,7 +477,7 @@ test_GIT_PAGER_overrides() {
                GIT_PAGER='wc >GIT_PAGER_used' &&
                export GIT_PAGER &&
                $full_command &&
-               test -e GIT_PAGER_used
+               test_path_is_file GIT_PAGER_used
        "
 }
 
@@ -489,7 +489,7 @@ test_doesnt_paginate() {
                GIT_PAGER='wc >GIT_PAGER_used' &&
                export GIT_PAGER &&
                $full_command &&
-               ! test -e GIT_PAGER_used
+               test_path_is_missing GIT_PAGER_used
        "
 }
 
index 96e163f084f471ea75e6d5b927a5edc6462e54d4..bfce05ac5dea71559e09f6023df2e5b6e1abdcbd 100755 (executable)
@@ -6,16 +6,15 @@
 test_description='git reset should cull empty subdirs'
 . ./test-lib.sh
 
-test_expect_success \
-    'creating initial files' \
-    'mkdir path0 &&
+test_expect_success 'creating initial files' '
+     mkdir path0 &&
      cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
      git add path0/COPYING &&
-     git commit -m add -a'
+     git commit -m add -a
+'
 
-test_expect_success \
-    'creating second files' \
-    'mkdir path1 &&
+test_expect_success 'creating second files' '
+     mkdir path1 &&
      mkdir path1/path2 &&
      cp "$TEST_DIRECTORY"/../COPYING path1/path2/COPYING &&
      cp "$TEST_DIRECTORY"/../COPYING path1/COPYING &&
@@ -25,39 +24,40 @@ test_expect_success \
      git add path1/COPYING &&
      git add COPYING &&
      git add path0/COPYING-TOO &&
-     git commit -m change -a'
+     git commit -m change -a
+'
 
-test_expect_success \
-    'resetting tree HEAD^' \
-    'git reset --hard HEAD^'
+test_expect_success 'resetting tree HEAD^' '
+     git reset --hard HEAD^
+'
 
-test_expect_success \
-    'checking initial files exist after rewind' \
-    'test -d path0 &&
-     test -f path0/COPYING'
+test_expect_success 'checking initial files exist after rewind' '
+     test -d path0 &&
+     test -f path0/COPYING
+'
 
-test_expect_success \
-    'checking lack of path1/path2/COPYING' \
-    '! test -f path1/path2/COPYING'
+test_expect_success 'checking lack of path1/path2/COPYING' '
+    ! test -f path1/path2/COPYING
+'
 
-test_expect_success \
-    'checking lack of path1/COPYING' \
-    '! test -f path1/COPYING'
+test_expect_success 'checking lack of path1/COPYING' '
+    ! test -f path1/COPYING
+'
 
-test_expect_success \
-    'checking lack of COPYING' \
-    '! test -f COPYING'
+test_expect_success 'checking lack of COPYING' '
+     ! test -f COPYING
+'
 
-test_expect_success \
-    'checking checking lack of path1/COPYING-TOO' \
-    '! test -f path0/COPYING-TOO'
+test_expect_success 'checking checking lack of path1/COPYING-TOO' '
+     ! test -f path0/COPYING-TOO
+'
 
-test_expect_success \
-    'checking lack of path1/path2' \
-    '! test -d path1/path2'
+test_expect_success 'checking lack of path1/path2' '
+     ! test -d path1/path2
+'
 
-test_expect_success \
-    'checking lack of path1' \
-    '! test -d path1'
+test_expect_success 'checking lack of path1' '
+     ! test -d path1
+'
 
 test_done
index 22161b3b2d5f7c42d827580802b00f97882b875f..b1affb001f599cbfdc074d2a19bf8816a76c4c32 100755 (executable)
@@ -70,27 +70,27 @@ check_changes () {
 
 test_expect_success 'reset --hard message' '
        hex=$(git log -1 --format="%h") &&
-       git reset --hard > .actual &&
-       echo HEAD is now at $hex $(commit_msg) > .expected &&
+       git reset --hard >.actual &&
+       echo HEAD is now at $hex $(commit_msg) >.expected &&
        test_i18ncmp .expected .actual
 '
 
 test_expect_success 'reset --hard message (ISO8859-1 logoutputencoding)' '
        hex=$(git log -1 --format="%h") &&
-       git -c "i18n.logOutputEncoding=$test_encoding" reset --hard > .actual &&
-       echo HEAD is now at $hex $(commit_msg $test_encoding) > .expected &&
+       git -c "i18n.logOutputEncoding=$test_encoding" reset --hard >.actual &&
+       echo HEAD is now at $hex $(commit_msg $test_encoding) >.expected &&
        test_i18ncmp .expected .actual
 '
 
->.diff_expect
->.cached_expect
-cat >.cat_expect <<EOF
-secondfile:
-1st line 2nd file
-2nd line 2nd file
-EOF
-
 test_expect_success 'giving a non existing revision should fail' '
+       >.diff_expect &&
+       >.cached_expect &&
+       cat >.cat_expect <<-\EOF &&
+       secondfile:
+       1st line 2nd file
+       2nd line 2nd file
+       EOF
+
        test_must_fail git reset aaaaaa &&
        test_must_fail git reset --mixed aaaaaa &&
        test_must_fail git reset --soft aaaaaa &&
@@ -107,8 +107,7 @@ test_expect_success 'reset --soft with unmerged index should fail' '
        git rm --cached -- un
 '
 
-test_expect_success \
-       'giving paths with options different than --mixed should fail' '
+test_expect_success 'giving paths with options different than --mixed should fail' '
        test_must_fail git reset --soft -- first &&
        test_must_fail git reset --hard -- first &&
        test_must_fail git reset --soft HEAD^ -- first &&
@@ -128,8 +127,7 @@ test_expect_success 'giving unrecognized options should fail' '
        check_changes $head5
 '
 
-test_expect_success \
-       'trying to do reset --soft with pending merge should fail' '
+test_expect_success 'trying to do reset --soft with pending merge should fail' '
        git branch branch1 &&
        git branch branch2 &&
 
@@ -152,8 +150,7 @@ test_expect_success \
        check_changes $head5
 '
 
-test_expect_success \
-       'trying to do reset --soft with pending checkout merge should fail' '
+test_expect_success 'trying to do reset --soft with pending checkout merge should fail' '
        git branch branch3 &&
        git branch branch4 &&
 
@@ -175,8 +172,7 @@ test_expect_success \
        check_changes $head5
 '
 
-test_expect_success \
-       'resetting to HEAD with no changes should succeed and do nothing' '
+test_expect_success 'resetting to HEAD with no changes should succeed and do nothing' '
        git reset --hard &&
                check_changes $head5 &&
        git reset --hard HEAD &&
@@ -195,39 +191,38 @@ test_expect_success \
                check_changes $head5
 '
 
->.diff_expect
-cat >.cached_expect <<EOF
-diff --git a/secondfile b/secondfile
-index $head5p1s..$head5s 100644
---- a/secondfile
-+++ b/secondfile
-@@ -1 +1,2 @@
--2nd file
-+1st line 2nd file
-+2nd line 2nd file
-EOF
-cat >.cat_expect <<EOF
-secondfile:
-1st line 2nd file
-2nd line 2nd file
-EOF
 test_expect_success '--soft reset only should show changes in diff --cached' '
+       >.diff_expect &&
+       cat >.cached_expect <<-EOF &&
+       diff --git a/secondfile b/secondfile
+       index $head5p1s..$head5s 100644
+       --- a/secondfile
+       +++ b/secondfile
+       @@ -1 +1,2 @@
+       -2nd file
+       +1st line 2nd file
+       +2nd line 2nd file
+       EOF
+       cat >.cat_expect <<-\EOF &&
+       secondfile:
+       1st line 2nd file
+       2nd line 2nd file
+       EOF
        git reset --soft HEAD^ &&
        check_changes $head5p1 &&
        test "$(git rev-parse ORIG_HEAD)" = \
                        $head5
 '
 
->.diff_expect
->.cached_expect
-cat >.cat_expect <<EOF
-secondfile:
-1st line 2nd file
-2nd line 2nd file
-3rd line 2nd file
-EOF
-test_expect_success \
-       'changing files and redo the last commit should succeed' '
+test_expect_success 'changing files and redo the last commit should succeed' '
+       >.diff_expect &&
+       >.cached_expect &&
+       cat >.cat_expect <<-\EOF &&
+       secondfile:
+       1st line 2nd file
+       2nd line 2nd file
+       3rd line 2nd file
+       EOF
        echo "3rd line 2nd file" >>secondfile &&
        git commit -a -C ORIG_HEAD &&
        head4=$(git rev-parse --verify HEAD) &&
@@ -236,56 +231,54 @@ test_expect_success \
                        $head5
 '
 
->.diff_expect
->.cached_expect
-cat >.cat_expect <<EOF
-first:
-1st file
-2nd line 1st file
-second:
-2nd file
-EOF
-test_expect_success \
-       '--hard reset should change the files and undo commits permanently' '
+test_expect_success '--hard reset should change the files and undo commits permanently' '
+       >.diff_expect &&
+       >.cached_expect &&
+       cat >.cat_expect <<-\EOF &&
+       first:
+       1st file
+       2nd line 1st file
+       second:
+       2nd file
+       EOF
        git reset --hard HEAD~2 &&
        check_changes $head5p2 &&
        test "$(git rev-parse ORIG_HEAD)" = \
                        $head4
 '
 
->.diff_expect
-cat >.cached_expect <<EOF
-diff --git a/first b/first
-deleted file mode 100644
-index $head5p2f..0000000
---- a/first
-+++ /dev/null
-@@ -1,2 +0,0 @@
--1st file
--2nd line 1st file
-diff --git a/second b/second
-deleted file mode 100644
-index $head5p1s..0000000
---- a/second
-+++ /dev/null
-@@ -1 +0,0 @@
--2nd file
-diff --git a/secondfile b/secondfile
-new file mode 100644
-index 0000000..$head5s
---- /dev/null
-+++ b/secondfile
-@@ -0,0 +1,2 @@
-+1st line 2nd file
-+2nd line 2nd file
-EOF
-cat >.cat_expect <<EOF
-secondfile:
-1st line 2nd file
-2nd line 2nd file
-EOF
-test_expect_success \
-       'redoing changes adding them without commit them should succeed' '
+test_expect_success 'redoing changes adding them without commit them should succeed' '
+       >.diff_expect &&
+       cat >.cached_expect <<-EOF &&
+       diff --git a/first b/first
+       deleted file mode 100644
+       index $head5p2f..0000000
+       --- a/first
+       +++ /dev/null
+       @@ -1,2 +0,0 @@
+       -1st file
+       -2nd line 1st file
+       diff --git a/second b/second
+       deleted file mode 100644
+       index $head5p1s..0000000
+       --- a/second
+       +++ /dev/null
+       @@ -1 +0,0 @@
+       -2nd file
+       diff --git a/secondfile b/secondfile
+       new file mode 100644
+       index 0000000..$head5s
+       --- /dev/null
+       +++ b/secondfile
+       @@ -0,0 +1,2 @@
+       +1st line 2nd file
+       +2nd line 2nd file
+       EOF
+       cat >.cat_expect <<-\EOF &&
+       secondfile:
+       1st line 2nd file
+       2nd line 2nd file
+       EOF
        git rm first &&
        git mv second secondfile &&
 
@@ -295,46 +288,45 @@ test_expect_success \
        check_changes $head5p2
 '
 
-cat >.diff_expect <<EOF
-diff --git a/first b/first
-deleted file mode 100644
-index $head5p2f..0000000
---- a/first
-+++ /dev/null
-@@ -1,2 +0,0 @@
--1st file
--2nd line 1st file
-diff --git a/second b/second
-deleted file mode 100644
-index $head5p1s..0000000
---- a/second
-+++ /dev/null
-@@ -1 +0,0 @@
--2nd file
-EOF
->.cached_expect
-cat >.cat_expect <<EOF
-secondfile:
-1st line 2nd file
-2nd line 2nd file
-EOF
 test_expect_success '--mixed reset to HEAD should unadd the files' '
+       cat >.diff_expect <<-EOF &&
+       diff --git a/first b/first
+       deleted file mode 100644
+       index $head5p2f..0000000
+       --- a/first
+       +++ /dev/null
+       @@ -1,2 +0,0 @@
+       -1st file
+       -2nd line 1st file
+       diff --git a/second b/second
+       deleted file mode 100644
+       index $head5p1s..0000000
+       --- a/second
+       +++ /dev/null
+       @@ -1 +0,0 @@
+       -2nd file
+       EOF
+       >.cached_expect &&
+       cat >.cat_expect <<-\EOF &&
+       secondfile:
+       1st line 2nd file
+       2nd line 2nd file
+       EOF
        git reset &&
        check_changes $head5p2 &&
        test "$(git rev-parse ORIG_HEAD)" = $head5p2
 '
 
->.diff_expect
->.cached_expect
-cat >.cat_expect <<EOF
-secondfile:
-1st line 2nd file
-2nd line 2nd file
-EOF
 test_expect_success 'redoing the last two commits should succeed' '
+       >.diff_expect &&
+       >.cached_expect &&
+       cat >.cat_expect <<-\EOF &&
+       secondfile:
+       1st line 2nd file
+       2nd line 2nd file
+       EOF
        git add secondfile &&
        git reset --hard $head5p2 &&
-
        git rm first &&
        git mv second secondfile &&
        git commit -a -m "remove 1st and rename 2nd" &&
@@ -347,15 +339,15 @@ test_expect_success 'redoing the last two commits should succeed' '
        check_changes $head5
 '
 
->.diff_expect
->.cached_expect
-cat >.cat_expect <<EOF
-secondfile:
-1st line 2nd file
-2nd line 2nd file
-3rd line in branch2
-EOF
 test_expect_success '--hard reset to HEAD should clear a failed merge' '
+       >.diff_expect &&
+       >.cached_expect &&
+       cat >.cat_expect <<-\EOF &&
+       secondfile:
+       1st line 2nd file
+       2nd line 2nd file
+       3rd line in branch2
+       EOF
        git branch branch1 &&
        git branch branch2 &&
 
@@ -373,15 +365,14 @@ test_expect_success '--hard reset to HEAD should clear a failed merge' '
        check_changes $head3
 '
 
->.diff_expect
->.cached_expect
-cat >.cat_expect <<EOF
-secondfile:
-1st line 2nd file
-2nd line 2nd file
-EOF
-test_expect_success \
-       '--hard reset to ORIG_HEAD should clear a fast-forward merge' '
+test_expect_success '--hard reset to ORIG_HEAD should clear a fast-forward merge' '
+       >.diff_expect &&
+       >.cached_expect &&
+       cat >.cat_expect <<-\EOF &&
+       secondfile:
+       1st line 2nd file
+       2nd line 2nd file
+       EOF
        git reset --hard HEAD^ &&
        check_changes $head5 &&
 
@@ -395,25 +386,25 @@ test_expect_success \
 '
 
 test_expect_success 'test --mixed <paths>' '
-       echo 1 > file1 &&
-       echo 2 > file2 &&
+       echo 1 >file1 &&
+       echo 2 >file2 &&
        git add file1 file2 &&
        test_tick &&
        git commit -m files &&
        before1=$(git rev-parse --short HEAD:file1) &&
        before2=$(git rev-parse --short HEAD:file2) &&
        git rm file2 &&
-       echo 3 > file3 &&
-       echo 4 > file4 &&
-       echo 5 > file1 &&
+       echo 3 >file3 &&
+       echo 4 >file4 &&
+       echo 5 >file1 &&
        after1=$(git rev-parse --short $(git hash-object file1)) &&
        after4=$(git rev-parse --short $(git hash-object file4)) &&
        git add file1 file3 file4 &&
        git reset HEAD -- file1 file2 file3 &&
        test_must_fail git diff --quiet &&
-       git diff > output &&
+       git diff >output &&
 
-       cat > expect <<-EOF &&
+       cat >expect <<-EOF &&
        diff --git a/file1 b/file1
        index $before1..$after1 100644
        --- a/file1
@@ -431,9 +422,9 @@ test_expect_success 'test --mixed <paths>' '
        EOF
 
        test_cmp expect output &&
-       git diff --cached > output &&
+       git diff --cached >output &&
 
-       cat > cached_expect <<-EOF &&
+       cat >cached_expect <<-EOF &&
        diff --git a/file4 b/file4
        new file mode 100644
        index 0000000..$after4
@@ -447,7 +438,6 @@ test_expect_success 'test --mixed <paths>' '
 '
 
 test_expect_success 'test resetting the index at give paths' '
-
        mkdir sub &&
        >sub/file1 &&
        >sub/file2 &&
@@ -460,7 +450,6 @@ test_expect_success 'test resetting the index at give paths' '
        echo "$U" &&
        test_must_fail git diff-index --cached --exit-code "$T" &&
        test "$T" != "$U"
-
 '
 
 test_expect_success 'resetting an unmodified path is a no-op' '
@@ -470,14 +459,13 @@ test_expect_success 'resetting an unmodified path is a no-op' '
        git diff-index --cached --exit-code HEAD
 '
 
-cat > expect << EOF
-Unstaged changes after reset:
-M      file2
-EOF
-
 test_expect_success '--mixed refreshes the index' '
-       echo 123 >> file2 &&
-       git reset --mixed HEAD > output &&
+       cat >expect <<-\EOF &&
+       Unstaged changes after reset:
+       M       file2
+       EOF
+       echo 123 >>file2 &&
+       git reset --mixed HEAD >output &&
        test_i18ncmp expect output
 '
 
@@ -498,7 +486,6 @@ test_expect_success 'resetting specific path that is unmerged' '
 '
 
 test_expect_success 'disambiguation (1)' '
-
        git reset --hard &&
        >secondfile &&
        git add secondfile &&
@@ -507,11 +494,9 @@ test_expect_success 'disambiguation (1)' '
        test -z "$(git diff --cached --name-only)" &&
        test -f secondfile &&
        test_must_be_empty secondfile
-
 '
 
 test_expect_success 'disambiguation (2)' '
-
        git reset --hard &&
        >secondfile &&
        git add secondfile &&
@@ -519,11 +504,9 @@ test_expect_success 'disambiguation (2)' '
        test_must_fail git reset secondfile &&
        test -n "$(git diff --cached --name-only -- secondfile)" &&
        test ! -f secondfile
-
 '
 
 test_expect_success 'disambiguation (3)' '
-
        git reset --hard &&
        >secondfile &&
        git add secondfile &&
@@ -532,11 +515,9 @@ test_expect_success 'disambiguation (3)' '
        test_must_fail git diff --quiet &&
        test -z "$(git diff --cached --name-only)" &&
        test ! -f secondfile
-
 '
 
 test_expect_success 'disambiguation (4)' '
-
        git reset --hard &&
        >secondfile &&
        git add secondfile &&
index 4d62b9b00fa919c2ec29872438d7a170a5605ee3..b36a93056fd8ea5b0fc489d92c3bacf3633c3498 100755 (executable)
@@ -33,8 +33,7 @@ fill () {
 
 
 test_expect_success setup '
-
-       fill x y z > same &&
+       fill x y z >same &&
        fill 1 2 3 4 5 6 7 8 >one &&
        fill a b c d e >two &&
        git add same one two &&
@@ -56,14 +55,13 @@ test_expect_success setup '
 
        git checkout -b simple master &&
        rm -f one &&
-       fill a c e > two &&
+       fill a c e >two &&
        git commit -a -m "Simple D one, M two" &&
 
        git checkout master
 '
 
-test_expect_success "checkout from non-existing branch" '
-
+test_expect_success 'checkout from non-existing branch' '
        git checkout -b delete-me master &&
        git update-ref -d --no-deref refs/heads/delete-me &&
        test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
@@ -71,8 +69,7 @@ test_expect_success "checkout from non-existing branch" '
        test refs/heads/master = "$(git symbolic-ref HEAD)"
 '
 
-test_expect_success "checkout with dirty tree without -m" '
-
+test_expect_success 'checkout with dirty tree without -m' '
        fill 0 1 2 3 4 5 6 7 8 >one &&
        if git checkout side
        then
@@ -81,11 +78,9 @@ test_expect_success "checkout with dirty tree without -m" '
        else
                echo "happy - failed correctly"
        fi
-
 '
 
-test_expect_success "checkout with unrelated dirty tree without -m" '
-
+test_expect_success 'checkout with unrelated dirty tree without -m' '
        git checkout -f master &&
        fill 0 1 2 3 4 5 6 7 8 >same &&
        cp same kept &&
@@ -95,13 +90,12 @@ test_expect_success "checkout with unrelated dirty tree without -m" '
        test_cmp messages.expect messages
 '
 
-test_expect_success "checkout -m with dirty tree" '
-
+test_expect_success 'checkout -m with dirty tree' '
        git checkout -f master &&
        git clean -f &&
 
        fill 0 1 2 3 4 5 6 7 8 >one &&
-       git checkout -m side > messages &&
+       git checkout -m side >messages &&
 
        test "$(git symbolic-ref HEAD)" = "refs/heads/side" &&
 
@@ -120,8 +114,7 @@ test_expect_success "checkout -m with dirty tree" '
        test_must_be_empty current.index
 '
 
-test_expect_success "checkout -m with dirty tree, renamed" '
-
+test_expect_success 'checkout -m with dirty tree, renamed' '
        git checkout -f master && git clean -f &&
 
        fill 1 2 3 4 5 7 8 >one &&
@@ -139,11 +132,9 @@ test_expect_success "checkout -m with dirty tree, renamed" '
        ! test -f one &&
        git diff --cached >current &&
        test_must_be_empty current
-
 '
 
 test_expect_success 'checkout -m with merge conflict' '
-
        git checkout -f master && git clean -f &&
 
        fill 1 T 3 4 5 6 S 8 >one &&
@@ -166,10 +157,10 @@ test_expect_success 'checkout -m with merge conflict' '
 '
 
 test_expect_success 'format of merge conflict from checkout -m' '
+       git checkout -f master &&
+       git clean -f &&
 
-       git checkout -f master && git clean -f &&
-
-       fill b d > two &&
+       fill b d >two &&
        git checkout -m simple &&
 
        git ls-files >current &&
@@ -190,10 +181,11 @@ test_expect_success 'format of merge conflict from checkout -m' '
 '
 
 test_expect_success 'checkout --merge --conflict=diff3 <branch>' '
+       git checkout -f master &&
+       git reset --hard &&
+       git clean -f &&
 
-       git checkout -f master && git reset --hard && git clean -f &&
-
-       fill b d > two &&
+       fill b d >two &&
        git checkout --merge --conflict=diff3 simple &&
 
        cat <<-EOF >expect &&
@@ -216,8 +208,9 @@ test_expect_success 'checkout --merge --conflict=diff3 <branch>' '
 '
 
 test_expect_success 'switch to another branch while carrying a deletion' '
-
-       git checkout -f master && git reset --hard && git clean -f &&
+       git checkout -f master &&
+       git reset --hard &&
+       git clean -f &&
        git rm two &&
 
        test_must_fail git checkout simple 2>errs &&
@@ -228,10 +221,10 @@ test_expect_success 'switch to another branch while carrying a deletion' '
 '
 
 test_expect_success 'checkout to detach HEAD (with advice declined)' '
-
        git config advice.detachedHead false &&
        rev=$(git rev-parse --short renamer^) &&
-       git checkout -f renamer && git clean -f &&
+       git checkout -f renamer &&
+       git clean -f &&
        git checkout renamer^ 2>messages &&
        test_i18ngrep "HEAD is now at $rev" messages &&
        test_line_count = 1 messages &&
@@ -250,7 +243,8 @@ test_expect_success 'checkout to detach HEAD (with advice declined)' '
 test_expect_success 'checkout to detach HEAD' '
        git config advice.detachedHead true &&
        rev=$(git rev-parse --short renamer^) &&
-       git checkout -f renamer && git clean -f &&
+       git checkout -f renamer &&
+       git clean -f &&
        GIT_TEST_GETTEXT_POISON=false git checkout renamer^ 2>messages &&
        grep "HEAD is now at $rev" messages &&
        test_line_count -gt 1 messages &&
@@ -267,8 +261,8 @@ test_expect_success 'checkout to detach HEAD' '
 '
 
 test_expect_success 'checkout to detach HEAD with branchname^' '
-
-       git checkout -f master && git clean -f &&
+       git checkout -f master &&
+       git clean -f &&
        git checkout renamer^ &&
        H=$(git rev-parse --verify HEAD) &&
        M=$(git show-ref -s --verify refs/heads/master) &&
@@ -283,8 +277,8 @@ test_expect_success 'checkout to detach HEAD with branchname^' '
 '
 
 test_expect_success 'checkout to detach HEAD with :/message' '
-
-       git checkout -f master && git clean -f &&
+       git checkout -f master &&
+       git clean -f &&
        git checkout ":/Initial" &&
        H=$(git rev-parse --verify HEAD) &&
        M=$(git show-ref -s --verify refs/heads/master) &&
@@ -299,8 +293,8 @@ test_expect_success 'checkout to detach HEAD with :/message' '
 '
 
 test_expect_success 'checkout to detach HEAD with HEAD^0' '
-
-       git checkout -f master && git clean -f &&
+       git checkout -f master &&
+       git clean -f &&
        git checkout HEAD^0 &&
        H=$(git rev-parse --verify HEAD) &&
        M=$(git show-ref -s --verify refs/heads/master) &&
@@ -315,7 +309,6 @@ test_expect_success 'checkout to detach HEAD with HEAD^0' '
 '
 
 test_expect_success 'checkout with ambiguous tag/branch names' '
-
        git tag both side &&
        git branch both master &&
        git reset --hard &&
@@ -327,11 +320,9 @@ test_expect_success 'checkout with ambiguous tag/branch names' '
        test "z$H" = "z$M" &&
        name=$(git symbolic-ref HEAD 2>/dev/null) &&
        test "z$name" = zrefs/heads/both
-
 '
 
 test_expect_success 'checkout with ambiguous tag/branch names' '
-
        git reset --hard &&
        git checkout master &&
 
@@ -351,26 +342,19 @@ test_expect_success 'checkout with ambiguous tag/branch names' '
        else
                : happy
        fi
-
 '
 
 test_expect_success 'switch branches while in subdirectory' '
-
        git reset --hard &&
        git checkout master &&
 
        mkdir subs &&
-       (
-               cd subs &&
-               git checkout side
-       ) &&
+       git -C subs checkout side &&
        ! test -f subs/one &&
        rm -fr subs
-
 '
 
 test_expect_success 'checkout specific path while in subdirectory' '
-
        git reset --hard &&
        git checkout side &&
        mkdir subs &&
@@ -380,30 +364,26 @@ test_expect_success 'checkout specific path while in subdirectory' '
 
        git checkout master &&
        mkdir -p subs &&
-       (
-               cd subs &&
-               git checkout side -- bero
-       ) &&
+       git -C subs checkout side -- bero &&
        test -f subs/bero
-
 '
 
-test_expect_success \
-    'checkout w/--track sets up tracking' '
+test_expect_success 'checkout w/--track sets up tracking' '
     git config branch.autosetupmerge false &&
     git checkout master &&
     git checkout --track -b track1 &&
     test "$(git config branch.track1.remote)" &&
-    test "$(git config branch.track1.merge)"'
+    test "$(git config branch.track1.merge)"
+'
 
-test_expect_success \
-    'checkout w/autosetupmerge=always sets up tracking' '
+test_expect_success 'checkout w/autosetupmerge=always sets up tracking' '
     test_when_finished git config branch.autosetupmerge false &&
     git config branch.autosetupmerge always &&
     git checkout master &&
     git checkout -b track2 &&
     test "$(git config branch.track2.remote)" &&
-    test "$(git config branch.track2.merge)"'
+    test "$(git config branch.track2.merge)"
+'
 
 test_expect_success 'checkout w/--track from non-branch HEAD fails' '
     git checkout master^0 &&
@@ -435,8 +415,7 @@ test_expect_success 'detach a symbolic link HEAD' '
     test "z$(git rev-parse --verify refs/heads/master)" = "z$here"
 '
 
-test_expect_success \
-    'checkout with --track fakes a sensible -b <name>' '
+test_expect_success 'checkout with --track fakes a sensible -b <name>' '
     git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" &&
     git update-ref refs/remotes/origin/koala/bear renamer &&
 
@@ -457,9 +436,9 @@ test_expect_success \
     test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
 '
 
-test_expect_success \
-    'checkout with --track, but without -b, fails with too short tracked name' '
-    test_must_fail git checkout --track renamer'
+test_expect_success 'checkout with --track, but without -b, fails with too short tracked name' '
+    test_must_fail git checkout --track renamer
+'
 
 setup_conflicting_index () {
        rm -f .git/index &&
@@ -609,7 +588,6 @@ test_expect_success 'failing checkout -b should not break working tree' '
        test $(git symbolic-ref HEAD) = refs/heads/master &&
        git diff --exit-code &&
        git diff --cached --exit-code
-
 '
 
 test_expect_success 'switch out of non-branch' '
index dc3e9c8c88b1d1037e355155b9325b4d4ded27db..905957bd0a1681265f6064a65b319c1c3fa95484 100755 (executable)
@@ -13,7 +13,7 @@ test_expect_success 'empty name and missing email' '
                sane_unset GIT_AUTHOR_EMAIL &&
                GIT_AUTHOR_NAME= &&
                test_must_fail git commit --allow-empty -m foo 2>err &&
-               test_i18ngrep ! null err
+               test_i18ngrep ! "(null)" err
        )
 '
 
index 53c883531e440c0d55db95134bab5a17e96a71e1..b2def8bb1600de5b9459328667f4ded635fdf9f7 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git maintenance builtin'
 . ./test-lib.sh
 
 GIT_TEST_COMMIT_GRAPH=0
+GIT_TEST_MULTI_PACK_INDEX=0
 
 test_expect_success 'help text' '
        test_expect_code 129 git maintenance -h 2>err &&
@@ -52,6 +53,43 @@ test_expect_success 'run --task=<task>' '
        test_subcommand git commit-graph write --split --reachable --no-progress <run-both.txt
 '
 
+test_expect_success 'core.commitGraph=false prevents write process' '
+       GIT_TRACE2_EVENT="$(pwd)/no-commit-graph.txt" \
+               git -c core.commitGraph=false maintenance run \
+               --task=commit-graph 2>/dev/null &&
+       test_subcommand ! git commit-graph write --split --reachable --no-progress \
+               <no-commit-graph.txt
+'
+
+test_expect_success 'commit-graph auto condition' '
+       COMMAND="maintenance run --task=commit-graph --auto --quiet" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/cg-no.txt" \
+               git -c maintenance.commit-graph.auto=1 $COMMAND &&
+       GIT_TRACE2_EVENT="$(pwd)/cg-negative-means-yes.txt" \
+               git -c maintenance.commit-graph.auto="-1" $COMMAND &&
+
+       test_commit first &&
+
+       GIT_TRACE2_EVENT="$(pwd)/cg-zero-means-no.txt" \
+               git -c maintenance.commit-graph.auto=0 $COMMAND &&
+       GIT_TRACE2_EVENT="$(pwd)/cg-one-satisfied.txt" \
+               git -c maintenance.commit-graph.auto=1 $COMMAND &&
+
+       git commit --allow-empty -m "second" &&
+       git commit --allow-empty -m "third" &&
+
+       GIT_TRACE2_EVENT="$(pwd)/cg-two-satisfied.txt" \
+               git -c maintenance.commit-graph.auto=2 $COMMAND &&
+
+       COMMIT_GRAPH_WRITE="git commit-graph write --split --reachable --no-progress" &&
+       test_subcommand ! $COMMIT_GRAPH_WRITE <cg-no.txt &&
+       test_subcommand $COMMIT_GRAPH_WRITE <cg-negative-means-yes.txt &&
+       test_subcommand ! $COMMIT_GRAPH_WRITE <cg-zero-means-no.txt &&
+       test_subcommand $COMMIT_GRAPH_WRITE <cg-one-satisfied.txt &&
+       test_subcommand $COMMIT_GRAPH_WRITE <cg-two-satisfied.txt
+'
+
 test_expect_success 'run --task=bogus' '
        test_must_fail git maintenance run --task=bogus 2>err &&
        test_i18ngrep "is not a valid task" err
@@ -62,4 +100,188 @@ test_expect_success 'run --task duplicate' '
        test_i18ngrep "cannot be selected multiple times" err
 '
 
+test_expect_success 'run --task=prefetch with no remotes' '
+       git maintenance run --task=prefetch 2>err &&
+       test_must_be_empty err
+'
+
+test_expect_success 'prefetch multiple remotes' '
+       git clone . clone1 &&
+       git clone . clone2 &&
+       git remote add remote1 "file://$(pwd)/clone1" &&
+       git remote add remote2 "file://$(pwd)/clone2" &&
+       git -C clone1 switch -c one &&
+       git -C clone2 switch -c two &&
+       test_commit -C clone1 one &&
+       test_commit -C clone2 two &&
+       GIT_TRACE2_EVENT="$(pwd)/run-prefetch.txt" git maintenance run --task=prefetch 2>/dev/null &&
+       fetchargs="--prune --no-tags --no-write-fetch-head --recurse-submodules=no --refmap= --quiet" &&
+       test_subcommand git fetch remote1 $fetchargs +refs/heads/\\*:refs/prefetch/remote1/\\* <run-prefetch.txt &&
+       test_subcommand git fetch remote2 $fetchargs +refs/heads/\\*:refs/prefetch/remote2/\\* <run-prefetch.txt &&
+       test_path_is_missing .git/refs/remotes &&
+       git log prefetch/remote1/one &&
+       git log prefetch/remote2/two &&
+       git fetch --all &&
+       test_cmp_rev refs/remotes/remote1/one refs/prefetch/remote1/one &&
+       test_cmp_rev refs/remotes/remote2/two refs/prefetch/remote2/two
+'
+
+test_expect_success 'loose-objects task' '
+       # Repack everything so we know the state of the object dir
+       git repack -adk &&
+
+       # Hack to stop maintenance from running during "git commit"
+       echo in use >.git/objects/maintenance.lock &&
+
+       # Assuming that "git commit" creates at least one loose object
+       test_commit create-loose-object &&
+       rm .git/objects/maintenance.lock &&
+
+       ls .git/objects >obj-dir-before &&
+       test_file_not_empty obj-dir-before &&
+       ls .git/objects/pack/*.pack >packs-before &&
+       test_line_count = 1 packs-before &&
+
+       # The first run creates a pack-file
+       # but does not delete loose objects.
+       git maintenance run --task=loose-objects &&
+       ls .git/objects >obj-dir-between &&
+       test_cmp obj-dir-before obj-dir-between &&
+       ls .git/objects/pack/*.pack >packs-between &&
+       test_line_count = 2 packs-between &&
+       ls .git/objects/pack/loose-*.pack >loose-packs &&
+       test_line_count = 1 loose-packs &&
+
+       # The second run deletes loose objects
+       # but does not create a pack-file.
+       git maintenance run --task=loose-objects &&
+       ls .git/objects >obj-dir-after &&
+       cat >expect <<-\EOF &&
+       info
+       pack
+       EOF
+       test_cmp expect obj-dir-after &&
+       ls .git/objects/pack/*.pack >packs-after &&
+       test_cmp packs-between packs-after
+'
+
+test_expect_success 'maintenance.loose-objects.auto' '
+       git repack -adk &&
+       GIT_TRACE2_EVENT="$(pwd)/trace-lo1.txt" \
+               git -c maintenance.loose-objects.auto=1 maintenance \
+               run --auto --task=loose-objects 2>/dev/null &&
+       test_subcommand ! git prune-packed --quiet <trace-lo1.txt &&
+       printf data-A | git hash-object -t blob --stdin -w &&
+       GIT_TRACE2_EVENT="$(pwd)/trace-loA" \
+               git -c maintenance.loose-objects.auto=2 \
+               maintenance run --auto --task=loose-objects 2>/dev/null &&
+       test_subcommand ! git prune-packed --quiet <trace-loA &&
+       printf data-B | git hash-object -t blob --stdin -w &&
+       GIT_TRACE2_EVENT="$(pwd)/trace-loB" \
+               git -c maintenance.loose-objects.auto=2 \
+               maintenance run --auto --task=loose-objects 2>/dev/null &&
+       test_subcommand git prune-packed --quiet <trace-loB &&
+       GIT_TRACE2_EVENT="$(pwd)/trace-loC" \
+               git -c maintenance.loose-objects.auto=2 \
+               maintenance run --auto --task=loose-objects 2>/dev/null &&
+       test_subcommand git prune-packed --quiet <trace-loC
+'
+
+test_expect_success 'incremental-repack task' '
+       packDir=.git/objects/pack &&
+       for i in $(test_seq 1 5)
+       do
+               test_commit $i || return 1
+       done &&
+
+       # Create three disjoint pack-files with size BIG, small, small.
+       echo HEAD~2 | git pack-objects --revs $packDir/test-1 &&
+       test_tick &&
+       git pack-objects --revs $packDir/test-2 <<-\EOF &&
+       HEAD~1
+       ^HEAD~2
+       EOF
+       test_tick &&
+       git pack-objects --revs $packDir/test-3 <<-\EOF &&
+       HEAD
+       ^HEAD~1
+       EOF
+       rm -f $packDir/pack-* &&
+       rm -f $packDir/loose-* &&
+       ls $packDir/*.pack >packs-before &&
+       test_line_count = 3 packs-before &&
+
+       # the job repacks the two into a new pack, but does not
+       # delete the old ones.
+       git maintenance run --task=incremental-repack &&
+       ls $packDir/*.pack >packs-between &&
+       test_line_count = 4 packs-between &&
+
+       # the job deletes the two old packs, and does not write
+       # a new one because the batch size is not high enough to
+       # pack the largest pack-file.
+       git maintenance run --task=incremental-repack &&
+       ls .git/objects/pack/*.pack >packs-after &&
+       test_line_count = 2 packs-after
+'
+
+test_expect_success EXPENSIVE 'incremental-repack 2g limit' '
+       for i in $(test_seq 1 5)
+       do
+               test-tool genrandom foo$i $((512 * 1024 * 1024 + 1)) >>big ||
+               return 1
+       done &&
+       git add big &&
+       git commit -m "Add big file (1)" &&
+
+       # ensure any possible loose objects are in a pack-file
+       git maintenance run --task=loose-objects &&
+
+       rm big &&
+       for i in $(test_seq 6 10)
+       do
+               test-tool genrandom foo$i $((512 * 1024 * 1024 + 1)) >>big ||
+               return 1
+       done &&
+       git add big &&
+       git commit -m "Add big file (2)" &&
+
+       # ensure any possible loose objects are in a pack-file
+       git maintenance run --task=loose-objects &&
+
+       # Now run the incremental-repack task and check the batch-size
+       GIT_TRACE2_EVENT="$(pwd)/run-2g.txt" git maintenance run \
+               --task=incremental-repack 2>/dev/null &&
+       test_subcommand git multi-pack-index repack \
+                --no-progress --batch-size=2147483647 <run-2g.txt
+'
+
+test_expect_success 'maintenance.incremental-repack.auto' '
+       git repack -adk &&
+       git config core.multiPackIndex true &&
+       git multi-pack-index write &&
+       GIT_TRACE2_EVENT="$(pwd)/midx-init.txt" git \
+               -c maintenance.incremental-repack.auto=1 \
+               maintenance run --auto --task=incremental-repack 2>/dev/null &&
+       test_subcommand ! git multi-pack-index write --no-progress <midx-init.txt &&
+       test_commit A &&
+       git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
+       HEAD
+       ^HEAD~1
+       EOF
+       GIT_TRACE2_EVENT=$(pwd)/trace-A git \
+               -c maintenance.incremental-repack.auto=2 \
+               maintenance run --auto --task=incremental-repack 2>/dev/null &&
+       test_subcommand ! git multi-pack-index write --no-progress <trace-A &&
+       test_commit B &&
+       git pack-objects --revs .git/objects/pack/pack <<-\EOF &&
+       HEAD
+       ^HEAD~1
+       EOF
+       GIT_TRACE2_EVENT=$(pwd)/trace-B git \
+               -c maintenance.incremental-repack.auto=2 \
+               maintenance run --auto --task=incremental-repack 2>/dev/null &&
+       test_subcommand git multi-pack-index write --no-progress <trace-B
+'
+
 test_done
diff --git a/t/t9304-fast-import-marks.sh b/t/t9304-fast-import-marks.sh
new file mode 100755 (executable)
index 0000000..d4359db
--- /dev/null
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='test exotic situations with marks'
+. ./test-lib.sh
+
+test_expect_success 'setup dump of basic history' '
+       test_commit one &&
+       git fast-export --export-marks=marks HEAD >dump
+'
+
+test_expect_success 'setup large marks file' '
+       # normally a marks file would have a lot of useful, unique
+       # marks. But for our purposes, just having a lot of nonsense
+       # ones is fine. Start at 1024 to avoid clashing with marks
+       # legitimately used in our tiny dump.
+       blob=$(git rev-parse HEAD:one.t) &&
+       for i in $(test_seq 1024 16384)
+       do
+               echo ":$i $blob"
+       done >>marks
+'
+
+test_expect_success 'import with large marks file' '
+       git fast-import --import-marks=marks <dump
+'
+
+test_expect_success 'setup dump with submodule' '
+       git submodule add "$PWD" sub &&
+       git commit -m "add submodule" &&
+       git fast-export HEAD >dump
+'
+
+test_expect_success 'setup submodule mapping with large id' '
+       old=$(git rev-parse HEAD:sub) &&
+       new=$(echo $old | sed s/./a/g) &&
+       echo ":12345 $old" >from &&
+       echo ":12345 $new" >to
+'
+
+test_expect_success 'import with submodule mapping' '
+       git init dst &&
+       git -C dst fast-import \
+               --rewrite-submodules-from=sub:../from \
+               --rewrite-submodules-to=sub:../to \
+               <dump &&
+       git -C dst rev-parse HEAD:sub >actual &&
+       echo "$new" >expect &&
+       test_cmp expect actual
+'
+
+test_done
index 67ff2711f5f5fd9ab95cbf05df8f38868e24bbec..a3abd778f93164ed35d169d8cbb37d9ab82b628a 100755 (executable)
@@ -67,7 +67,7 @@ test_expect_success 'import main, no branch detection' '
        (
                cd "$git" &&
                git log --oneline --graph --decorate --all &&
-               git rev-list master >wc &&
+               git rev-list master -- >wc &&
                test_line_count = 4 wc
        )
 '
@@ -78,7 +78,7 @@ test_expect_success 'import branch1, no branch detection' '
        (
                cd "$git" &&
                git log --oneline --graph --decorate --all &&
-               git rev-list master >wc &&
+               git rev-list master -- >wc &&
                test_line_count = 2 wc
        )
 '
@@ -89,7 +89,7 @@ test_expect_success 'import branch2, no branch detection' '
        (
                cd "$git" &&
                git log --oneline --graph --decorate --all &&
-               git rev-list master >wc &&
+               git rev-list master -- >wc &&
                test_line_count = 2 wc
        )
 '
@@ -100,7 +100,7 @@ test_expect_success 'import depot, no branch detection' '
        (
                cd "$git" &&
                git log --oneline --graph --decorate --all &&
-               git rev-list master >wc &&
+               git rev-list master -- >wc &&
                test_line_count = 8 wc
        )
 '
@@ -114,7 +114,7 @@ test_expect_success 'import depot, branch detection' '
                git log --oneline --graph --decorate --all &&
 
                # 4 main commits
-               git rev-list master >wc &&
+               git rev-list master -- >wc &&
                test_line_count = 4 wc &&
 
                # 3 main, 1 integrate, 1 on branch2
@@ -137,7 +137,7 @@ test_expect_success 'import depot, branch detection, branchList branch definitio
                git log --oneline --graph --decorate --all &&
 
                # 4 main commits
-               git rev-list master >wc &&
+               git rev-list master -- >wc &&
                test_line_count = 4 wc &&
 
                # 3 main, 1 integrate, 1 on branch2
index 7194fb285566d7863668f8a3c66f7e55536ccb74..6b3cb0414aa08cb62817ba9f0710e6db4d282787 100755 (executable)
@@ -68,7 +68,8 @@ EOF
                cd "$git" &&
                change=$(last_shelved_change) &&
                git p4 unshelve $change &&
-               git show refs/remotes/p4-unshelved/$change | grep -q "Further description" &&
+               git show refs/remotes/p4-unshelved/$change >actual &&
+               grep -q "Further description" actual &&
                git cherry-pick refs/remotes/p4-unshelved/$change &&
                test_path_is_file file2 &&
                test_cmp file1 "$cli"/file1 &&
index caf4e9101f99c5567caef5c02689a105a6f0d86f..2be9190425bb1dc6f9ab0eb1b8e030c99e0d2920 100755 (executable)
@@ -1055,13 +1055,13 @@ test_expect_success 'teardown after filtering matching refs' '
        git -C otherrepo branch -D matching/branch-in-other
 '
 
-test_expect_success '__git_refs - for-each-ref format specifiers in prefix' '
+test_expect_success PREPARE_FOR_MAIN_BRANCH '__git_refs - for-each-ref format specifiers in prefix' '
        cat >expected <<-EOF &&
        evil-%%-%42-%(refname)..master
        EOF
        (
-               cur="evil-%%-%42-%(refname)..mas" &&
-               __git_refs "" "" "evil-%%-%42-%(refname).." mas >"$actual"
+               cur="evil-%%-%42-%(refname)..mai" &&
+               __git_refs "" "" "evil-%%-%42-%(refname).." mai >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
@@ -1360,6 +1360,58 @@ test_expect_success 'git checkout - a later --no-guess overrides previous --gues
        EOF
 '
 
+test_expect_success 'git checkout - with checkout.guess = false, only completes refs' '
+       test_config checkout.guess false &&
+       test_completion "git checkout " <<-\EOF
+       HEAD Z
+       master Z
+       matching-branch Z
+       matching-tag Z
+       other/branch-in-other Z
+       other/master-in-other Z
+       EOF
+'
+
+test_expect_success 'git checkout - with checkout.guess = true, completes refs and unique remote branches for DWIM' '
+       test_config checkout.guess true &&
+       test_completion "git checkout " <<-\EOF
+       HEAD Z
+       branch-in-other Z
+       master Z
+       master-in-other Z
+       matching-branch Z
+       matching-tag Z
+       other/branch-in-other Z
+       other/master-in-other Z
+       EOF
+'
+
+test_expect_success 'git checkout - a later --guess overrides previous checkout.guess = false, complete refs and unique remote branches for DWIM' '
+       test_config checkout.guess false &&
+       test_completion "git checkout --guess " <<-\EOF
+       HEAD Z
+       branch-in-other Z
+       master Z
+       master-in-other Z
+       matching-branch Z
+       matching-tag Z
+       other/branch-in-other Z
+       other/master-in-other Z
+       EOF
+'
+
+test_expect_success 'git checkout - a later --no-guess overrides previous checkout.guess = true, complete only refs' '
+       test_config checkout.guess true &&
+       test_completion "git checkout --no-guess " <<-\EOF
+       HEAD Z
+       master Z
+       matching-branch Z
+       matching-tag Z
+       other/branch-in-other Z
+       other/master-in-other Z
+       EOF
+'
+
 test_expect_success 'git switch - with --detach, complete all references' '
        test_completion "git switch --detach " <<-\EOF
        HEAD Z
index 8d59b90348ea898ca983e8bbd0742de495a8d36d..4a35bde145cc71073a74e2e53d35f32447de4c6c 100644 (file)
@@ -952,13 +952,7 @@ test_expect_code () {
 # - not all diff versions understand "-u"
 
 test_cmp() {
-       test $# -eq 2 || BUG "test_cmp requires two arguments"
-       if ! eval "$GIT_TEST_CMP" '"$@"'
-       then
-               test "x$1" = x- || test -e "$1" || BUG "test_cmp '$1' missing"
-               test "x$2" = x- || test -e "$2" || BUG "test_cmp '$2' missing"
-               return 1
-       fi
+       eval "$GIT_TEST_CMP" '"$@"'
 }
 
 # Check that the given config key has the expected value.
@@ -987,13 +981,7 @@ test_cmp_config() {
 # test_cmp_bin - helper to compare binary files
 
 test_cmp_bin() {
-       test $# -eq 2 || BUG "test_cmp_bin requires two arguments"
-       if ! cmp "$@"
-       then
-               test "x$1" = x- || test -e "$1" || BUG "test_cmp_bin '$1' missing"
-               test "x$2" = x- || test -e "$2" || BUG "test_cmp_bin '$2' missing"
-               return 1
-       fi
+       cmp "$@"
 }
 
 # Use this instead of test_cmp to compare files that contain expected and
index ef31f400374755015ccec300bdb450021aeccc17..fa347ed3e1d476291abcb4186705543f52914d70 100644 (file)
@@ -499,6 +499,12 @@ then
        export GIT_INDEX_VERSION
 fi
 
+if test -n "$GIT_TEST_PERL_FATAL_WARNINGS"
+then
+       GIT_PERL_FATAL_WARNINGS=1
+       export GIT_PERL_FATAL_WARNINGS
+fi
+
 # Add libc MALLOC and MALLOC_PERTURB test
 # only if we are not executing the test with valgrind
 if test -n "$valgrind" ||
@@ -769,15 +775,17 @@ match_pattern_list () {
 }
 
 match_test_selector_list () {
+       operation="$1"
+       shift
        title="$1"
        shift
        arg="$1"
        shift
        test -z "$1" && return 0
 
-       # Both commas and whitespace are accepted as separators.
+       # Commas are accepted as separators.
        OLDIFS=$IFS
-       IFS='   ,'
+       IFS=','
        set -- $1
        IFS=$OLDIFS
 
@@ -805,13 +813,13 @@ match_test_selector_list () {
                        *-*)
                                if expr "z${selector%%-*}" : "z[0-9]*[^0-9]" >/dev/null
                                then
-                                       echo "error: $title: invalid non-numeric in range" \
+                                       echo "error: $operation: invalid non-numeric in range" \
                                                "start: '$orig_selector'" >&2
                                        exit 1
                                fi
                                if expr "z${selector#*-}" : "z[0-9]*[^0-9]" >/dev/null
                                then
-                                       echo "error: $title: invalid non-numeric in range" \
+                                       echo "error: $operation: invalid non-numeric in range" \
                                                "end: '$orig_selector'" >&2
                                        exit 1
                                fi
@@ -819,9 +827,11 @@ match_test_selector_list () {
                        *)
                                if expr "z$selector" : "z[0-9]*[^0-9]" >/dev/null
                                then
-                                       echo "error: $title: invalid non-numeric in test" \
-                                               "selector: '$orig_selector'" >&2
-                                       exit 1
+                                       case "$title" in *${selector}*)
+                                               include=$positive
+                                               ;;
+                                       esac
+                                       continue
                                fi
                esac
 
@@ -1031,7 +1041,7 @@ test_skip () {
                skipped_reason="GIT_SKIP_TESTS"
        fi
        if test -z "$to_skip" && test -n "$run_list" &&
-          ! match_test_selector_list '--run' $test_count "$run_list"
+          ! match_test_selector_list '--run' "$1" $test_count "$run_list"
        then
                to_skip=t
                skipped_reason="--run"
@@ -1058,7 +1068,6 @@ test_skip () {
                                "      <skipped message=\"$message\" />"
                fi
 
-               say_color skip >&3 "skipping test: $@"
                say_color skip "ok $test_count # skip $1 ($skipped_reason)"
                : true
                ;;
@@ -1702,3 +1711,10 @@ test_lazy_prereq SHA1 '
 test_lazy_prereq REBASE_P '
        test -z "$GIT_TEST_SKIP_REBASE_P"
 '
+# Special-purpose prereq for transitioning to a new default branch name:
+# Some tests need more than just a mindless (case-preserving) s/master/main/g
+# replacement. The non-trivial adjustments are guarded behind this
+# prerequisite, acting kind of as a feature flag
+test_lazy_prereq PREPARE_FOR_MAIN_BRANCH '
+       test "$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME" = main
+'
diff --git a/templates/hooks--push-to-checkout.sample b/templates/hooks--push-to-checkout.sample
new file mode 100755 (executable)
index 0000000..af5a0c0
--- /dev/null
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+# An example hook script to update a checked-out tree on a git push.
+#
+# This hook is invoked by git-receive-pack(1) when it reacts to git
+# push and updates reference(s) in its repository, and when the push
+# tries to update the branch that is currently checked out and the
+# receive.denyCurrentBranch configuration variable is set to
+# updateInstead.
+#
+# By default, such a push is refused if the working tree and the index
+# of the remote repository has any difference from the currently
+# checked out commit; when both the working tree and the index match
+# the current commit, they are updated to match the newly pushed tip
+# of the branch. This hook is to be used to override the default
+# behaviour; however the code below reimplements the default behaviour
+# as a starting point for convenient modification.
+#
+# The hook receives the commit with which the tip of the current
+# branch is going to be updated:
+commit=$1
+
+# It can exit with a non-zero status to refuse the push (when it does
+# so, it must not modify the index or the working tree).
+die () {
+       echo >&2 "$*"
+       exit 1
+}
+
+# Or it can make any necessary changes to the working tree and to the
+# index to bring them to the desired state when the tip of the current
+# branch is updated to the new commit, and exit with a zero status.
+#
+# For example, the hook can simply run git read-tree -u -m HEAD "$1"
+# in order to emulate git fetch that is run in the reverse direction
+# with git push, as the two-tree form of git read-tree -u -m is
+# essentially the same as git switch or git checkout that switches
+# branches while keeping the local changes in the working tree that do
+# not interfere with the difference between the branches.
+
+# The below is a more-or-less exact translation to shell of the C code
+# for the default behaviour for git's push-to-checkout hook defined in
+# the push_to_deploy() function in builtin/receive-pack.c.
+#
+# Note that the hook will be executed from the repository directory,
+# not from the working tree, so if you want to perform operations on
+# the working tree, you will have to adapt your code accordingly, e.g.
+# by adding "cd .." or using relative paths.
+
+if ! git update-index -q --ignore-submodules --refresh
+then
+       die "Up-to-date check failed"
+fi
+
+if ! git diff-files --quiet --ignore-submodules --
+then
+       die "Working directory has unstaged changes"
+fi
+
+# This is a rough translation of:
+#
+#   head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX
+if git cat-file -e HEAD 2>/dev/null
+then
+       head=HEAD
+else
+       head=$(git hash-object -t tree --stdin </dev/null)
+fi
+
+if ! git diff-index --quiet --cached --ignore-submodules $head --
+then
+       die "Working directory has staged changes"
+fi
+
+if ! git read-tree -u -m "$commit"
+then
+       die "Could not update working tree to new HEAD"
+fi
index b573b6c730ccf49d2805a27b14957504f2218f41..5f6e0b3bd874dba714c107dffebd423ef25d10b4 100644 (file)
@@ -827,6 +827,10 @@ static int push_update_ref_status(struct strbuf *buf,
                        status = REF_STATUS_REJECT_STALE;
                        FREE_AND_NULL(msg);
                }
+               else if (!strcmp(msg, "remote ref updated since checkout")) {
+                       status = REF_STATUS_REJECT_REMOTE_UPDATED;
+                       FREE_AND_NULL(msg);
+               }
                else if (!strcmp(msg, "forced update")) {
                        forced = 1;
                        FREE_AND_NULL(msg);
@@ -934,6 +938,11 @@ static void set_common_push_options(struct transport *transport,
                if (set_helper_option(transport, TRANS_OPT_ATOMIC, "true") != 0)
                        die(_("helper %s does not support --atomic"), name);
 
+       if (flags & TRANSPORT_PUSH_FORCE_IF_INCLUDES)
+               if (set_helper_option(transport, TRANS_OPT_FORCE_IF_INCLUDES, "true") != 0)
+                       die(_("helper %s does not support --%s"),
+                           name, TRANS_OPT_FORCE_IF_INCLUDES);
+
        if (flags & TRANSPORT_PUSH_OPTIONS) {
                struct string_list_item *item;
                for_each_string_list_item(item, transport->push_options)
@@ -967,6 +976,7 @@ static int push_refs_with_push(struct transport *transport,
                case REF_STATUS_REJECT_NONFASTFORWARD:
                case REF_STATUS_REJECT_STALE:
                case REF_STATUS_REJECT_ALREADY_EXISTS:
+               case REF_STATUS_REJECT_REMOTE_UPDATED:
                        if (atomic) {
                                reject_atomic_push(remote_refs, mirror);
                                string_list_clear(&cas_options, 0);
index ffe21158455116972dd8b0cf77167948fb6e66e6..47da955e4fe4548199c28365d3f894bd0b1a81b7 100644 (file)
@@ -633,6 +633,11 @@ static int print_one_push_report(struct ref *ref, const char *dest, int count,
                                 "stale info",
                                 report, porcelain, summary_width);
                break;
+       case REF_STATUS_REJECT_REMOTE_UPDATED:
+               print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+                                "remote ref updated since checkout",
+                                report, porcelain, summary_width);
+               break;
        case REF_STATUS_REJECT_SHALLOW:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
                                 "new shallow roots not allowed",
@@ -743,6 +748,8 @@ void transport_print_push_status(const char *dest, struct ref *refs,
                        *reject_reasons |= REJECT_FETCH_FIRST;
                } else if (ref->status == REF_STATUS_REJECT_NEEDS_FORCE) {
                        *reject_reasons |= REJECT_NEEDS_FORCE;
+               } else if (ref->status == REF_STATUS_REJECT_REMOTE_UPDATED) {
+                       *reject_reasons |= REJECT_REF_NEEDS_UPDATE;
                }
        }
        free(head);
@@ -1185,6 +1192,7 @@ static int run_pre_push_hook(struct transport *transport,
                if (!r->peer_ref) continue;
                if (r->status == REF_STATUS_REJECT_NONFASTFORWARD) continue;
                if (r->status == REF_STATUS_REJECT_STALE) continue;
+               if (r->status == REF_STATUS_REJECT_REMOTE_UPDATED) continue;
                if (r->status == REF_STATUS_UPTODATE) continue;
 
                strbuf_reset(&buf);
index ca409ea1e407c1c67e70e764e734946f7e22372e..24558c027d61bba1dd99b17a1c9d1cd484b1cb8a 100644 (file)
@@ -136,6 +136,7 @@ struct transport {
 #define TRANSPORT_PUSH_ATOMIC                  (1<<13)
 #define TRANSPORT_PUSH_OPTIONS                 (1<<14)
 #define TRANSPORT_RECURSE_SUBMODULES_ONLY      (1<<15)
+#define TRANSPORT_PUSH_FORCE_IF_INCLUDES       (1<<16)
 
 int transport_summary_width(const struct ref *refs);
 
@@ -208,6 +209,9 @@ void transport_check_allowed(const char *type);
 /* Request atomic (all-or-nothing) updates when pushing */
 #define TRANS_OPT_ATOMIC "atomic"
 
+/* Require remote changes to be integrated locally. */
+#define TRANS_OPT_FORCE_IF_INCLUDES "force-if-includes"
+
 /**
  * Returns 0 if the option was used, non-zero otherwise. Prints a
  * message to stderr if the option is not used.
@@ -217,11 +221,12 @@ int transport_set_option(struct transport *transport, const char *name,
 void transport_set_verbosity(struct transport *transport, int verbosity,
        int force_progress);
 
-#define REJECT_NON_FF_HEAD     0x01
-#define REJECT_NON_FF_OTHER    0x02
-#define REJECT_ALREADY_EXISTS  0x04
-#define REJECT_FETCH_FIRST     0x08
-#define REJECT_NEEDS_FORCE     0x10
+#define REJECT_NON_FF_HEAD      0x01
+#define REJECT_NON_FF_OTHER     0x02
+#define REJECT_ALREADY_EXISTS   0x04
+#define REJECT_FETCH_FIRST      0x08
+#define REJECT_NEEDS_FORCE      0x10
+#define REJECT_REF_NEEDS_UPDATE 0x20
 
 int transport_push(struct repository *repo,
                   struct transport *connection,
diff --git a/usage.c b/usage.c
index 58fb5fff5f245c33898e27c85296a4a47325c34e..06665823a21ef9a42edcd7c4c2febf7bdae8013e 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -108,33 +108,33 @@ static int die_is_recursing_builtin(void)
 
 /* If we are in a dlopen()ed .so write to a global variable would segfault
  * (ugh), so keep things static. */
-static NORETURN_PTR void (*usage_routine)(const char *err, va_list params) = usage_builtin;
-static NORETURN_PTR void (*die_routine)(const char *err, va_list params) = die_builtin;
-static void (*error_routine)(const char *err, va_list params) = error_builtin;
-static void (*warn_routine)(const char *err, va_list params) = warn_builtin;
+static NORETURN_PTR report_fn usage_routine = usage_builtin;
+static NORETURN_PTR report_fn die_routine = die_builtin;
+static report_fn error_routine = error_builtin;
+static report_fn warn_routine = warn_builtin;
 static int (*die_is_recursing)(void) = die_is_recursing_builtin;
 
-void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params))
+void set_die_routine(NORETURN_PTR report_fn routine)
 {
        die_routine = routine;
 }
 
-void set_error_routine(void (*routine)(const char *err, va_list params))
+void set_error_routine(report_fn routine)
 {
        error_routine = routine;
 }
 
-void (*get_error_routine(void))(const char *err, va_list params)
+report_fn get_error_routine(void)
 {
        return error_routine;
 }
 
-void set_warn_routine(void (*routine)(const char *warn, va_list params))
+void set_warn_routine(report_fn routine)
 {
        warn_routine = routine;
 }
 
-void (*get_warn_routine(void))(const char *warn, va_list params)
+report_fn get_warn_routine(void)
 {
        return warn_routine;
 }
index fde02f225b2b9ed17a8246913c3bddfa6483c98b..3f81a2261c5e97e5cf50c1dc45ed196da8759f69 100644 (file)
@@ -23,6 +23,27 @@ IPATTERN("ada",
         "[a-zA-Z][a-zA-Z0-9_]*"
         "|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?"
         "|=>|\\.\\.|\\*\\*|:=|/=|>=|<=|<<|>>|<>"),
+PATTERNS("bash",
+        /* Optional leading indentation */
+        "^[ \t]*"
+        /* Start of captured text */
+        "("
+        "("
+            /* POSIX identifier with mandatory parentheses */
+            "[a-zA-Z_][a-zA-Z0-9_]*[ \t]*\\([ \t]*\\))"
+        "|"
+            /* Bashism identifier with optional parentheses */
+            "(function[ \t]+[a-zA-Z_][a-zA-Z0-9_]*(([ \t]*\\([ \t]*\\))|([ \t]+))"
+        ")"
+        /* Optional whitespace */
+        "[ \t]*"
+        /* Compound command starting with `{`, `(`, `((` or `[[` */
+        "(\\{|\\(\\(?|\\[\\[)"
+        /* End of captured text */
+        ")",
+        /* -- */
+        /* Characters not in the default $IFS value */
+        "[^ \t]+"),
 PATTERNS("dts",
         "!;\n"
         "!=\n"
@@ -147,7 +168,7 @@ PATTERNS("perl",
         "|=~|!~"
         "|<<|<>|<=>|>>"),
 PATTERNS("php",
-        "^[\t ]*(((public|protected|private|static)[\t ]+)*function.*)$\n"
+        "^[\t ]*(((public|protected|private|static|abstract|final)[\t ]+)*function.*)$\n"
         "^[\t ]*((((final|abstract)[\t ]+)?class|interface|trait).*)$",
         /* -- */
         "[a-zA-Z_][a-zA-Z0-9_]*"
@@ -165,7 +186,7 @@ PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
         "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
         "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"),
 PATTERNS("rust",
-        "^[\t ]*((pub(\\([^\\)]+\\))?[\t ]+)?((async|const|unsafe|extern([\t ]+\"[^\"]+\"))[\t ]+)?(struct|enum|union|mod|trait|fn|impl)[< \t]+[^;]*)$",
+        "^[\t ]*((pub(\\([^\\)]+\\))?[\t ]+)?((async|const|unsafe|extern([\t ]+\"[^\"]+\"))[\t ]+)?(struct|enum|union|mod|trait|fn|impl|macro_rules!)[< \t]+[^;]*)$",
         /* -- */
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[0-9][0-9_a-fA-Fiosuxz]*(\\.([0-9]*[eE][+-]?)?[0-9_fF]*)?"
@@ -200,7 +221,7 @@ PATTERNS("csharp",
         "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
 IPATTERN("css",
         "![:;][[:space:]]*$\n"
-        "^[_a-z0-9].*$",
+        "^[:[@.#]?[_a-z0-9].*$",
         /* -- */
         /*
          * This regex comes from W3C CSS specs. Should theoretically also
index 032e3a9f41a2f79eaab78ae36666b8b6218b3899..7a046051468f9e7e804d9eb7f0bd7eeeba6727db 100644 (file)
@@ -79,6 +79,10 @@ typedef struct s_mmbuffer {
 typedef struct s_xpparam {
        unsigned long flags;
 
+       /* -I<regex> */
+       regex_t **ignore_regex;
+       size_t ignore_regex_nr;
+
        /* See Documentation/diff-options.txt. */
        char **anchors;
        size_t anchors_nr;
index bd035139f954a61b5f3dc30d9fdedfbdb2a7972b..380eb728ed4ea579678195b66b856fb297b892cf 100644 (file)
@@ -998,7 +998,7 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
        return 0;
 }
 
-static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)
+static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)
 {
        xdchange_t *xch;
 
@@ -1019,6 +1019,46 @@ static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)
        }
 }
 
+static int record_matches_regex(xrecord_t *rec, xpparam_t const *xpp) {
+       regmatch_t regmatch;
+       int i;
+
+       for (i = 0; i < xpp->ignore_regex_nr; i++)
+               if (!regexec_buf(xpp->ignore_regex[i], rec->ptr, rec->size, 1,
+                                &regmatch, 0))
+                       return 1;
+
+       return 0;
+}
+
+static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,
+                                    xpparam_t const *xpp)
+{
+       xdchange_t *xch;
+
+       for (xch = xscr; xch; xch = xch->next) {
+               xrecord_t **rec;
+               int ignore = 1;
+               long i;
+
+               /*
+                * Do not override --ignore-blank-lines.
+                */
+               if (xch->ignore)
+                       continue;
+
+               rec = &xe->xdf1.recs[xch->i1];
+               for (i = 0; i < xch->chg1 && ignore; i++)
+                       ignore = record_matches_regex(rec[i], xpp);
+
+               rec = &xe->xdf2.recs[xch->i2];
+               for (i = 0; i < xch->chg2 && ignore; i++)
+                       ignore = record_matches_regex(rec[i], xpp);
+
+               xch->ignore = ignore;
+       }
+}
+
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
             xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
        xdchange_t *xscr;
@@ -1038,7 +1078,10 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
        }
        if (xscr) {
                if (xpp->flags & XDF_IGNORE_BLANK_LINES)
-                       xdl_mark_ignorable(xscr, &xe, xpp->flags);
+                       xdl_mark_ignorable_lines(xscr, &xe, xpp->flags);
+
+               if (xpp->ignore_regex)
+                       xdl_mark_ignorable_regex(xscr, &xe, xpp);
 
                if (ef(&xe, xscr, ecb, xecfg) < 0) {
 
index c7b35a9667f322c151bc8de276654ecd38974aef..e694bfd9e31d54f1925a730a75b0ef6d9a4e6d95 100644 (file)
@@ -235,6 +235,8 @@ static int fall_back_to_classic_diff(xpparam_t const *xpp, xdfenv_t *env,
                int line1, int count1, int line2, int count2)
 {
        xpparam_t xpparam;
+
+       memset(&xpparam, 0, sizeof(xpparam));
        xpparam.flags = xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
 
        return xdl_fall_back_diff(env, &xpparam,
index 3c5601b602a24cb6cd6e063466369b1a90a14f9f..20699a6f60547a88b5f07df0ab6651ccef5bff60 100644 (file)
@@ -318,6 +318,8 @@ static int fall_back_to_classic_diff(struct hashmap *map,
                int line1, int count1, int line2, int count2)
 {
        xpparam_t xpp;
+
+       memset(&xpp, 0, sizeof(xpp));
        xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
 
        return xdl_fall_back_diff(map->env, &xpp,