diff options
129 files changed, 3138 insertions, 1911 deletions
diff --git a/.gitignore b/.gitignore index 2cf3ca5808..711fce7e08 100644 --- a/.gitignore +++ b/.gitignore @@ -101,7 +101,9 @@ /git-quiltimport /git-read-tree /git-rebase +/git-rebase--am /git-rebase--interactive +/git-rebase--merge /git-receive-pack /git-reflog /git-relink diff --git a/Documentation/RelNotes/1.7.5.1.txt b/Documentation/RelNotes/1.7.5.1.txt new file mode 100644 index 0000000000..c6ebd76d19 --- /dev/null +++ b/Documentation/RelNotes/1.7.5.1.txt @@ -0,0 +1,47 @@ +Git v1.7.5.1 Release Notes +========================== + +Fixes since v1.7.5 +------------------ + + * When an object "$tree:$path" does not exist, if $path does exist in the + subtree of $tree that corresponds to the subdirectory the user is in, + git now suggests using "$tree:./$path" in addition to the advice to use + the full path from the root of the working tree. + + * The "--date=relative" output format used to say "X years, 12 months" + when it should have said "X+1 years". + + * The smart-HTTP transfer was broken in 1.7.5 when the client needs + to issue a small POST (which uses content-length) and then a large + POST (which uses chunked) back to back. + + * "git clean" used to fail on an empty directory that is not readable, + even though rmdir(2) could remove such a directory. Now we attempt it + as the last resort. + + * The "--dirstat" option of "diff" family of commands used to totally + ignore a change that only rearranged lines within a file. Such a + change now counts as at least a minimum but non zero change. + + * The "--dirstat" option of "diff" family of commands used to use the + pathname in the original, instead of the pathname in the result, + when renames are involved. + + * "git pack-object" did not take core.bigfilethreashold into account + (unlike fast-import); now it does. + + * "git reflog" ignored options like "--format=.." on the command line. + + * "git stash apply" used to refuse to work if there was any change in + the working tree, even when the change did not overlap with the change + the stash recorded. + + * "git stash apply @{99999}" was not diagnosed as an error, even when you + did not have that many stash entries. + + * An error message from "git send-email" to diagnose a broken SMTP + connection configuration lacked a space between "hello=<smtp-domain>" + and "port=<smtp-server-port>". + +And other minor fixes and documentation updates. diff --git a/Documentation/RelNotes/1.7.6.txt b/Documentation/RelNotes/1.7.6.txt index 3e08bd917e..9945ac9ef2 100644 --- a/Documentation/RelNotes/1.7.6.txt +++ b/Documentation/RelNotes/1.7.6.txt @@ -1,4 +1,4 @@ -Git v1.7.5 Release Notes (draft) +Git v1.7.6 Release Notes (draft) ======================== Updates since v1.7.5 @@ -6,34 +6,36 @@ Updates since v1.7.5 * Various git-svn updates. - * When an object "$tree:$path" does not exist, if $path does exist in the - subtree of $tree that corresponds to the subdirectory the user is in, - git now suggests using "$tree:./$path" in addition to the advice to use - the full path from the root of the working tree. + * Clean-up of the C part of i18n (but not l10n---please wait) + continues. * "git blame" learned "--abbrev[=<n>]" option to control the minimum number of hexdigits shown for commit object names. - * "git clean" used to fail on an empty directory that is not readable, - even though rmdir(2) could remove such a directory. Now we attempt it - as the last resort. + * "git diff -C -C" used to disable the rename detection entirely when + there are too many copy candidate paths in the tree; now it falls + back to "-C" when doing so would keep the copy candidate paths + under the rename detection limit. * "git format-patch" learned "--quiet" option to suppress the output of the names of generated files. + * "git log" and friends learned a new "--notes" option to replace the + "--show-notes" option. Unlike "--show-notes", "--notes=<ref>" does + not imply showing the default notes. + * "git merge" learned "-" as a short-hand for "the previous branch", just like the way "git checkout -" works. - * "git pack-object" now takes core.bigfilethreashold into account, just - like fast-imoprt does. - - * "git reflog" allows options like "--format=.." to be given. + * "git rev-list --count" used with "--cherry-mark" counts the cherry-picked + commits separately, producing more a useful output. - * "git stash apply" can now apply to a working tree with changes as long - as there is no overlapping change as the stash being applied. + * "git submodule update" learned "--force" option to get rid of local + changes in submodules and replace them with the up-to-date version. - * "git stash apply @{99999}" now is diagnosed as an error, unless you - really have that many stash entries. + * Compressed tarball gitweb generates is made without the timestamp of + the tarball generation; snapshot from the same tree should result in + a same tarball. Also contains various documentation updates. @@ -44,26 +46,25 @@ Fixes since v1.7.5 Unless otherwise noted, all the fixes in 1.7.5.X maintenance track are included in this release. - * The "--date=relative" output format used to say "X years, 12 months" - when it should have said "X+1 years". - (merge mg/x-years-12-months later) - * "git config" used to choke with an insanely long line. (merge ef/maint-strbuf-init later) - * The "--dirstat" option of "diff" family of commands used to totally - ignore a change that only rearranged lines within a file. Such a - change now counts as at least a minimum but non zero change. - - * The "--dirstat" option of "diff" family of commands used to use the - pathname in the original, instead of the pathname in the result, - when renames are involved. - (merge jh/dirstat for the above two later) + * "diff -M --cached" used to use unmerged path as a possible rename + source candidate, which made no sense. + (merge mz/maint-rename-unmerged later) * "git format-patch" when run with "--quiet" option used to produce a nonsense result that consists of alternating empty output. (merge early part of cn/format-patch-quiet later) + * "git format-patch" did not quote RFC822 special characters in the + email address (e.g From: Junio C. Hamano <jch@example.com>, not + From: "Junio C. Hamano" <jch@example.com>). + (merge jk/format-patch-quote-special-in-from later) + + * "git mergetool" did not handle conflicted submoudules gracefully. + (merge jm/mergetool-submodules later) + * "git stash -p --no-keep-index" and "git stash --no-keep-index -p" now mean the same thing. (merge dm/stash-k-i-p later) @@ -75,5 +76,5 @@ included in this release. --- exec >/var/tmp/1 echo O=$(git describe master) -O=v1.7.5 +O=v1.7.5-184-g23f536c git shortlog --no-merges ^maint ^$O master diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index c6a5032912..938eccf2a5 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -344,50 +344,20 @@ MUA specific hints Some of patches I receive or pick up from the list share common patterns of breakage. Please make sure your MUA is set up -properly not to corrupt whitespaces. Here are two common ones -I have seen: +properly not to corrupt whitespaces. -* Empty context lines that do not have _any_ whitespace. +See the DISCUSSION section of git-format-patch(1) for hints on +checking your patch by mailing it to yourself and applying with +git-am(1). -* Non empty context lines that have one extra whitespace at the - beginning. - -One test you could do yourself if your MUA is set up correctly is: - -* Send the patch to yourself, exactly the way you would, except - To: and Cc: lines, which would not contain the list and - maintainer address. - -* Save that patch to a file in UNIX mailbox format. Call it say - a.patch. - -* Try to apply to the tip of the "master" branch from the - git.git public repository: - - $ git fetch http://kernel.org/pub/scm/git/git.git master:test-apply - $ git checkout test-apply - $ git reset --hard - $ git am a.patch - -If it does not apply correctly, there can be various reasons. - -* Your patch itself does not apply cleanly. That is _bad_ but - does not have much to do with your MUA. Please rebase the - patch appropriately. - -* Your MUA corrupted your patch; "am" would complain that - the patch does not apply. Look at .git/rebase-apply/ subdirectory and - see what 'patch' file contains and check for the common - corruption patterns mentioned above. - -* While you are at it, check what are in 'info' and - 'final-commit' files as well. If what is in 'final-commit' is - not exactly what you would want to see in the commit log - message, it is very likely that your maintainer would end up - hand editing the log message when he applies your patch. - Things like "Hi, this is my first patch.\n", if you really - want to put in the patch e-mail, should come after the - three-dash line that signals the end of the commit message. +While you are at it, check the resulting commit log message from +a trial run of applying the patch. If what is in the resulting +commit is not exactly what you would want to see, it is very +likely that your maintainer would end up hand editing the log +message when he applies your patch. Things like "Hi, this is my +first patch.\n", if you really want to put in the patch e-mail, +should come after the three-dash line that signals the end of the +commit message. Pine @@ -443,89 +413,10 @@ that or Gentoo did it.) So you need to set the it. -Thunderbird ------------ - -(A Large Angry SCM) - -By default, Thunderbird will both wrap emails as well as flag them as -being 'format=flowed', both of which will make the resulting email unusable -by git. - -Here are some hints on how to successfully submit patches inline using -Thunderbird. - -There are two different approaches. One approach is to configure -Thunderbird to not mangle patches. The second approach is to use -an external editor to keep Thunderbird from mangling the patches. - -Approach #1 (configuration): - -This recipe is current as of Thunderbird 2.0.0.19. Three steps: - 1. Configure your mail server composition as plain text - Edit...Account Settings...Composition & Addressing, - uncheck 'Compose Messages in HTML'. - 2. Configure your general composition window to not wrap - Edit..Preferences..Composition, wrap plain text messages at 0 - 3. Disable the use of format=flowed - Edit..Preferences..Advanced..Config Editor. Search for: - mailnews.send_plaintext_flowed - toggle it to make sure it is set to 'false'. - -After that is done, you should be able to compose email as you -otherwise would (cut + paste, git-format-patch | git-imap-send, etc), -and the patches should not be mangled. - -Approach #2 (external editor): - -This recipe appears to work with the current [*1*] Thunderbird from Suse. - -The following Thunderbird extensions are needed: - AboutConfig 0.5 - http://aboutconfig.mozdev.org/ - External Editor 0.7.2 - http://globs.org/articles.php?lng=en&pg=8 - -1) Prepare the patch as a text file using your method of choice. - -2) Before opening a compose window, use Edit->Account Settings to -uncheck the "Compose messages in HTML format" setting in the -"Composition & Addressing" panel of the account to be used to send the -patch. [*2*] - -3) In the main Thunderbird window, _before_ you open the compose window -for the patch, use Tools->about:config to set the following to the -indicated values: - mailnews.send_plaintext_flowed => false - mailnews.wraplength => 0 - -4) Open a compose window and click the external editor icon. - -5) In the external editor window, read in the patch file and exit the -editor normally. - -6) Back in the compose window: Add whatever other text you wish to the -message, complete the addressing and subject fields, and press send. - -7) Optionally, undo the about:config/account settings changes made in -steps 2 & 3. +Thunderbird, KMail, GMail +------------------------- - -[Footnotes] -*1* Version 1.0 (20041207) from the MozillaThunderbird-1.0-5 rpm of Suse -9.3 professional updates. - -*2* It may be possible to do this with about:config and the following -settings but I haven't tried, yet. - mail.html_compose => false - mail.identity.default.compose_html => false - mail.identity.id?.compose_html => false - -(Lukas Sandström) - -There is a script in contrib/thunderbird-patch-inline which can help -you include patches with Thunderbird in an easy way. To use it, do the -steps above and then use the script as the external editor. +See the MUA-SPECIFIC HINTS section of git-format-patch(1). Gnus ---- @@ -540,71 +431,3 @@ characters (most notably in people's names), and also whitespaces (fatal in patches). Running 'C-u g' to display the message in raw form before using '|' to run the pipe can work this problem around. - - -KMail ------ - -This should help you to submit patches inline using KMail. - -1) Prepare the patch as a text file. - -2) Click on New Mail. - -3) Go under "Options" in the Composer window and be sure that -"Word wrap" is not set. - -4) Use Message -> Insert file... and insert the patch. - -5) Back in the compose window: add whatever other text you wish to the -message, complete the addressing and subject fields, and press send. - - -Gmail ------ - -GMail does not appear to have any way to turn off line wrapping in the web -interface, so this will mangle any emails that you send. You can however -use "git send-email" and send your patches through the GMail SMTP server, or -use any IMAP email client to connect to the google IMAP server and forward -the emails through that. - -To use "git send-email" and send your patches through the GMail SMTP server, -edit ~/.gitconfig to specify your account settings: - -[sendemail] - smtpencryption = tls - smtpserver = smtp.gmail.com - smtpuser = user@gmail.com - smtppass = p4ssw0rd - smtpserverport = 587 - -Once your commits are ready to be sent to the mailing list, run the -following commands: - - $ git format-patch --cover-letter -M origin/master -o outgoing/ - $ edit outgoing/0000-* - $ git send-email outgoing/* - -To submit using the IMAP interface, first, edit your ~/.gitconfig to specify your -account settings: - -[imap] - folder = "[Gmail]/Drafts" - host = imaps://imap.gmail.com - user = user@gmail.com - pass = p4ssw0rd - port = 993 - sslverify = false - -You might need to instead use: folder = "[Google Mail]/Drafts" if you get an error -that the "Folder doesn't exist". - -Once your commits are ready to be sent to the mailing list, run the -following commands: - - $ git format-patch --cover-letter -M --stdout origin/master | git imap-send - -Just make sure to disable line wrapping in the email client (GMail web -interface will line wrap no matter what, so you need to use a real -IMAP client). diff --git a/Documentation/config.txt b/Documentation/config.txt index ce17805509..c96aec10d8 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -587,6 +587,8 @@ it will be treated as a shell command. For example, defining "gitk --all --not ORIG_HEAD". Note that shell commands will be executed from the top-level directory of a repository, which may not necessarily be the current directory. +'GIT_PREFIX' is set as returned by running 'git rev-parse --show-prefix' +from the original current directory. See linkgit:git-rev-parse[1]. am.keepcr:: If true, git-am will call git-mailsplit for patches in mbox format @@ -641,7 +643,7 @@ branch.<name>.remote:: branch.<name>.merge:: Defines, together with branch.<name>.remote, the upstream branch - for the given branch. It tells 'git fetch'/'git pull' which + for the given branch. It tells 'git fetch'/'git pull'/'git rebase' which branch to merge and can also affect 'git push' (see push.default). When in branch <name>, it tells 'git fetch' the default refspec to be marked for merging in FETCH_HEAD. The value is @@ -706,9 +708,16 @@ second is the background. The position of the attribute, if any, doesn't matter. color.diff:: - When set to `always`, always use colors in patch. - When false (or `never`), never. When set to `true` or `auto`, use - colors only when the output is to the terminal. Defaults to false. + Whether to use ANSI escape sequences to add color to patches. + If this is set to `always`, linkgit:git-diff[1], + linkgit:git-log[1], and linkgit:git-show[1] will use color + for all patches. If it is set to `true` or `auto`, those + commands will only use color when output is to the terminal. + Defaults to false. ++ +This does not affect linkgit:git-format-patch[1] nor the +'git-diff-{asterisk}' plumbing commands. Can be overridden on the +command line with the `--color[=<when>]` option. color.diff.<slot>:: Use customized color for diff colorization. `<slot>` specifies @@ -794,11 +803,15 @@ color.status.<slot>:: color.branch.<slot>. color.ui:: - When set to `always`, always use colors in all git commands which - are capable of colored output. When false (or `never`), never. When - set to `true` or `auto`, use colors only when the output is to the - terminal. When more specific variables of color.* are set, they always - take precedence over this setting. Defaults to false. + This variable determines the default value for variables such + as `color.diff` and `color.grep` that control the use of color + per command family. Its scope will expand as more commands learn + configuration to set a default for the `--color` option. Set it + to `always` if you want all output not intended for machine + consumption to use color, to `true` or `auto` if you want such + output to use color when written to the terminal, or to `false` or + `never` if you prefer git commands not to use color unless enabled + explicitly with some other configuration or the `--color` option. commit.status:: A boolean to enable/disable inclusion of status information in the diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index c93124be79..9ca565d708 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -72,6 +72,10 @@ endif::git-format-patch[] a cut-off percent (3% by default) are not shown. The cut-off percent can be set with `--dirstat=<limit>`. Changes in a child directory are not counted for the parent directory, unless `--cumulative` is used. ++ +Note that the `--dirstat` option computes the changes while ignoring +the amount of pure code movements within a file. In other words, +rearranging lines in a file is not counted as much as other changes. --dirstat-by-file[=<limit>]:: Same as `--dirstat`, but counts changed files instead of lines. @@ -120,12 +124,19 @@ any of those replacements occurred. --color[=<when>]:: Show colored diff. - The value must be always (the default), never, or auto. + The value must be `always` (the default for `<when>`), `never`, or `auto`. + The default value is `never`. +ifdef::git-diff[] + It can be changed by the `color.ui` and `color.diff` + configuration settings. +endif::git-diff[] --no-color:: - Turn off colored diff, even when the configuration file - gives the default to color output. - Same as `--color=never`. + Turn off colored diff. +ifdef::git-diff[] + This can be used to override configuration settings. +endif::git-diff[] + It is the same as `--color=never`. --word-diff[=<mode>]:: Show a word diff, using the <mode> to delimit changed words. @@ -239,7 +250,7 @@ ifdef::git-log[] For following files across renames while traversing history, see `--follow`. endif::git-log[] - If `n` is specified, it is a is a threshold on the similarity + If `n` is specified, it is a threshold on the similarity index (i.e. amount of addition/deletions compared to the file's size). For example, `-M90%` means git should consider a delete/add pair to be a rename if more than 90% of the file @@ -259,6 +270,19 @@ endif::git-log[] projects, so use it with caution. Giving more than one `-C` option has the same effect. +-D:: +--irreversible-delete:: + Omit the preimage for deletes, i.e. print only the header but not + the diff between the preimage and `/dev/null`. The resulting patch + is not meant to be applied with `patch` nor `git apply`; this is + solely for people who want to just concentrate on reviewing the + text after the change. In addition, the output obviously lack + enough information to apply such a patch in reverse, even manually, + hence the name of the option. ++ +When used together with `-B`, omit also the preimage in the deletion part +of a delete/create pair. + -l<num>:: The `-M` and `-C` options require O(n^2) processing time where n is the number of potential rename/copy targets. This diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 7eebbefe7b..35cb5d3f64 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -134,6 +134,8 @@ subdirectories. If some files could not be added because of errors indexing them, do not abort the operation, but continue adding the others. The command shall still exit with non-zero status. + The configuration variable `add.ignoreErrors` can be set to + true to make this the default behaviour. --ignore-missing:: This option can only be used together with --dry-run. By using diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 01db83039f..9d8fe0d261 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -51,9 +51,10 @@ OPTIONS message prior to committing. -x:: - When recording the commit, append to the original commit - message a note that indicates which commit this change - was cherry-picked from. Append the note only for cherry + When recording the commit, append a line that says + "(cherry picked from commit ...)" to the original commit + message in order to indicate which commit this change was + cherry-picked from. This is done only for cherry picks without conflicts. Do not use this option if you are cherry-picking from your private branch because the information is useless to the recipient. If on the diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt index 2c2ea12c5c..249249aac7 100644 --- a/Documentation/git-fast-import.txt +++ b/Documentation/git-fast-import.txt @@ -83,7 +83,7 @@ OPTIONS skips the file if it does not exist. --relative-marks:: - After specifying --relative-marks= the paths specified + After specifying --relative-marks the paths specified with --import-marks= and --export-marks= are relative to an internal directory in the current repository. In git-fast-import this means that the paths are relative @@ -93,7 +93,7 @@ OPTIONS --no-relative-marks:: Negates a previous --relative-marks. Allows for combining relative and non-relative marks by interweaving - --(no-)-relative-marks= with the --(import|export)-marks= + --(no-)-relative-marks with the --(import|export)-marks= options. --cat-blob-fd=<fd>:: diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index f4e959d662..d13c9b23f7 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -232,6 +232,233 @@ attachments, and sign off patches with configuration variables. ------------ +DISCUSSION +---------- + +The patch produced by 'git format-patch' is in UNIX mailbox format, +with a fixed "magic" time stamp to indicate that the file is output +from format-patch rather than a real mailbox, like so: + +------------ +From 8f72bad1baf19a53459661343e21d6491c3908d3 Mon Sep 17 00:00:00 2001 +From: Tony Luck <tony.luck@intel.com> +Date: Tue, 13 Jul 2010 11:42:54 -0700 +Subject: [PATCH] =?UTF-8?q?[IA64]=20Put=20ia64=20config=20files=20on=20the=20?= + =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20diet?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +arch/arm config files were slimmed down using a python script +(See commit c2330e286f68f1c408b4aa6515ba49d57f05beae comment) + +Do the same for ia64 so we can have sleek & trim looking +... +------------ + +Typically it will be placed in a MUA's drafts folder, edited to add +timely commentary that should not go in the changelog after the three +dashes, and then sent as a message whose body, in our example, starts +with "arch/arm config files were...". On the receiving end, readers +can save interesting patches in a UNIX mailbox and apply them with +linkgit:git-am[1]. + +When a patch is part of an ongoing discussion, the patch generated by +'git format-patch' can be tweaked to take advantage of the 'git am +--scissors' feature. After your response to the discussion comes a +line that consists solely of "`-- >8 --`" (scissors and perforation), +followed by the patch with unnecessary header fields removed: + +------------ +... +> So we should do such-and-such. + +Makes sense to me. How about this patch? + +-- >8 -- +Subject: [IA64] Put ia64 config files on the Uwe Kleine-König diet + +arch/arm config files were slimmed down using a python script +... +------------ + +When sending a patch this way, most often you are sending your own +patch, so in addition to the "`From $SHA1 $magic_timestamp`" marker you +should omit `From:` and `Date:` lines from the patch file. The patch +title is likely to be different from the subject of the discussion the +patch is in response to, so it is likely that you would want to keep +the Subject: line, like the example above. + +Checking for patch corruption +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Many mailers if not set up properly will corrupt whitespace. Here are +two common types of corruption: + +* Empty context lines that do not have _any_ whitespace. + +* Non-empty context lines that have one extra whitespace at the + beginning. + +One way to test if your MUA is set up correctly is: + +* Send the patch to yourself, exactly the way you would, except + with To: and Cc: lines that do not contain the list and + maintainer address. + +* Save that patch to a file in UNIX mailbox format. Call it a.patch, + say. + +* Apply it: + + $ git fetch <project> master:test-apply + $ git checkout test-apply + $ git reset --hard + $ git am a.patch + +If it does not apply correctly, there can be various reasons. + +* The patch itself does not apply cleanly. That is _bad_ but + does not have much to do with your MUA. You might want to rebase + the patch with linkgit:git-rebase[1] before regenerating it in + this case. + +* The MUA corrupted your patch; "am" would complain that + the patch does not apply. Look in the .git/rebase-apply/ subdirectory and + see what 'patch' file contains and check for the common + corruption patterns mentioned above. + +* While at it, check the 'info' and 'final-commit' files as well. + If what is in 'final-commit' is not exactly what you would want to + see in the commit log message, it is very likely that the + receiver would end up hand editing the log message when applying + your patch. Things like "Hi, this is my first patch.\n" in the + patch e-mail should come after the three-dash line that signals + the end of the commit message. + +MUA-SPECIFIC HINTS +------------------ +Here are some hints on how to successfully submit patches inline using +various mailers. + +GMail +~~~~~ +GMail does not have any way to turn off line wrapping in the web +interface, so it will mangle any emails that you send. You can however +use "git send-email" and send your patches through the GMail SMTP server, or +use any IMAP email client to connect to the google IMAP server and forward +the emails through that. + +For hints on using 'git send-email' to send your patches through the +GMail SMTP server, see the EXAMPLE section of linkgit:git-send-email[1]. + +For hints on submission using the IMAP interface, see the EXAMPLE +section of linkgit:git-imap-send[1]. + +Thunderbird +~~~~~~~~~~~ +By default, Thunderbird will both wrap emails as well as flag +them as being 'format=flowed', both of which will make the +resulting email unusable by git. + +There are three different approaches: use an add-on to turn off line wraps, +configure Thunderbird to not mangle patches, or use +an external editor to keep Thunderbird from mangling the patches. + +Approach #1 (add-on) +^^^^^^^^^^^^^^^^^^^^ + +Install the Toggle Word Wrap add-on that is available from +https://addons.mozilla.org/thunderbird/addon/toggle-word-wrap/ +It adds a menu entry "Enable Word Wrap" in the composer's "Options" menu +that you can tick off. Now you can compose the message as you otherwise do +(cut + paste, 'git format-patch' | 'git imap-send', etc), but you have to +insert line breaks manually in any text that you type. + +Approach #2 (configuration) +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Three steps: + +1. Configure your mail server composition as plain text: + Edit...Account Settings...Composition & Addressing, + uncheck "Compose Messages in HTML". + +2. Configure your general composition window to not wrap. ++ +In Thunderbird 2: +Edit..Preferences..Composition, wrap plain text messages at 0 ++ +In Thunderbird 3: +Edit..Preferences..Advanced..Config Editor. Search for +"mail.wrap_long_lines". +Toggle it to make sure it is set to `false`. + +3. Disable the use of format=flowed: +Edit..Preferences..Advanced..Config Editor. Search for +"mailnews.send_plaintext_flowed". +Toggle it to make sure it is set to `false`. + +After that is done, you should be able to compose email as you +otherwise would (cut + paste, 'git format-patch' | 'git imap-send', etc), +and the patches will not be mangled. + +Approach #3 (external editor) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following Thunderbird extensions are needed: +AboutConfig from http://aboutconfig.mozdev.org/ and +External Editor from http://globs.org/articles.php?lng=en&pg=8 + +1. Prepare the patch as a text file using your method of choice. + +2. Before opening a compose window, use Edit->Account Settings to + uncheck the "Compose messages in HTML format" setting in the + "Composition & Addressing" panel of the account to be used to + send the patch. + +3. In the main Thunderbird window, 'before' you open the compose + window for the patch, use Tools->about:config to set the + following to the indicated values: ++ +---------- + mailnews.send_plaintext_flowed => false + mailnews.wraplength => 0 +---------- + +4. Open a compose window and click the external editor icon. + +5. In the external editor window, read in the patch file and exit + the editor normally. + +Side note: it may be possible to do step 2 with +about:config and the following settings but no one's tried yet. + +---------- + mail.html_compose => false + mail.identity.default.compose_html => false + mail.identity.id?.compose_html => false +---------- + +There is a script in contrib/thunderbird-patch-inline which can help +you include patches with Thunderbird in an easy way. To use it, do the +steps above and then use the script as the external editor. + +KMail +~~~~~ +This should help you to submit patches inline using KMail. + +1. Prepare the patch as a text file. + +2. Click on New Mail. + +3. Go under "Options" in the Composer window and be sure that + "Word wrap" is not set. + +4. Use Message -> Insert file... and insert the patch. + +5. Back in the compose window: add whatever other text you wish to the + message, complete the addressing and subject fields, and press send. + + EXAMPLES -------- diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt index d3013d6a29..4e09708cc9 100644 --- a/Documentation/git-imap-send.txt +++ b/Documentation/git-imap-send.txt @@ -111,6 +111,31 @@ Using direct mode with SSL: .......................... +EXAMPLE +------- +To submit patches using GMail's IMAP interface, first, edit your ~/.gitconfig +to specify your account settings: + +--------- +[imap] + folder = "[Gmail]/Drafts" + host = imaps://imap.gmail.com + user = user@gmail.com + port = 993 + sslverify = false +--------- + +You might need to instead use: folder = "[Google Mail]/Drafts" if you get an error +that the "Folder doesn't exist". + +Once the commits are ready to be sent, run the following command: + + $ git format-patch --cover-letter -M --stdout origin/master | git imap-send + +Just make sure to disable line wrapping in the email client (GMail's web +interface will wrap lines no matter what, so you need to use a real +IMAP client). + CAUTION ------- It is still your responsibility to make sure that the email message @@ -124,6 +149,10 @@ Thunderbird in particular is known to be problematic. Thunderbird users may wish to visit this web page for more information: http://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email +SEE ALSO +-------- +linkgit:git-format-patch[1], linkgit:git-send-email[1], mbox(5) + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 2c84028838..de5c0d37a5 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -178,9 +178,9 @@ May be an unabbreviated ref name or a glob and may be specified multiple times. A warning will be issued for refs that do not exist, but a glob that does not match any refs is silently ignored. + -This setting can be disabled by the `--no-standard-notes` option, +This setting can be disabled by the `--no-notes` option, overridden by the 'GIT_NOTES_DISPLAY_REF' environment variable, -and supplemented by the `--show-notes` option. +and overridden by the `--notes=<ref>` option. GIT --- diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt index ba36ec04f4..b295bf8330 100644 --- a/Documentation/git-merge-base.txt +++ b/Documentation/git-merge-base.txt @@ -9,7 +9,8 @@ git-merge-base - Find as good common ancestors as possible for a merge SYNOPSIS -------- [verse] -'git merge-base' [-a|--all] [--octopus] <commit> <commit>... +'git merge-base' [-a|--all] <commit> <commit>... +'git merge-base' [-a|--all] --octopus <commit>... 'git merge-base' --independent <commit>... DESCRIPTION @@ -22,23 +23,21 @@ that does not have any better common ancestor is a 'best common ancestor', i.e. a 'merge base'. Note that there can be more than one merge base for a pair of commits. -Unless `--octopus` is given, among the two commits to compute the merge -base from, one is specified by the first commit argument on the command -line; the other commit is a (possibly hypothetical) commit that is a merge -across all the remaining commits on the command line. As the most common -special case, specifying only two commits on the command line means -computing the merge base between the given two commits. +OPERATION MODE +-------------- + +As the most common special case, specifying only two commits on the +command line means computing the merge base between the given two commits. + +More generally, among the two commits to compute the merge base from, +one is specified by the first commit argument on the command line; +the other commit is a (possibly hypothetical) commit that is a merge +across all the remaining commits on the command line. As a consequence, the 'merge base' is not necessarily contained in each of the commit arguments if more than two commits are specified. This is different from linkgit:git-show-branch[1] when used with the `--merge-base` option. -OPTIONS -------- --a:: ---all:: - Output all merge bases for the commits, instead of just one. - --octopus:: Compute the best common ancestors of all supplied commits, in preparation for an n-way merge. This mimics the behavior @@ -51,6 +50,12 @@ OPTIONS from any other. This mimics the behavior of 'git show-branch --independent'. +OPTIONS +------- +-a:: +--all:: + Output all merge bases for the commits, instead of just one. + DISCUSSION ---------- @@ -89,6 +94,9 @@ and the result of `git merge-base A M` is '1'. Commit '2' is also a common ancestor between 'A' and 'M', but '1' is a better common ancestor, because '2' is an ancestor of '1'. Hence, '2' is not a merge base. +The result of `git merge-base --octopus A B C` is '2', because '2' is +the best common ancestor of all commits. + When the history involves criss-cross merges, there can be more than one 'best' common ancestor for two commits. For example, with this topology: diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt index 296f314eae..913ecd8c43 100644 --- a/Documentation/git-notes.txt +++ b/Documentation/git-notes.txt @@ -57,8 +57,11 @@ list:: add:: Add notes for a given object (defaults to HEAD). Abort if the - object already has notes (use `-f` to overwrite an - existing note). + object already has notes (use `-f` to overwrite existing notes). + However, if you're using `add` interactively (using an editor + to supply the notes contents), then - instead of aborting - + the existing notes will be opened in the editor (like the `edit` + subcommand). copy:: Copy the notes for the first object onto the second object. diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 620d50e71f..9a075bc4d2 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git rebase' [-i | --interactive] [options] [--onto <newbase>] - <upstream> [<branch>] + [<upstream>] [<branch>] 'git rebase' [-i | --interactive] [options] --onto <newbase> --root [<branch>] @@ -21,6 +21,12 @@ If <branch> is specified, 'git rebase' will perform an automatic `git checkout <branch>` before doing anything else. Otherwise it remains on the current branch. +If <upstream> is not specified, the upstream configured in +branch.<name>.remote and branch.<name>.merge options will be used; see +linkgit:git-config[1] for details. If you are currently not on any +branch or if the current branch does not have a configured upstream, +the rebase will abort. + All changes made by commits in the current branch but that are not in <upstream> are saved to a temporary area. This is the same set of commits that would be shown by `git log <upstream>..HEAD` (or @@ -217,7 +223,8 @@ leave out at most one of A and B, in which case it defaults to HEAD. <upstream>:: Upstream branch to compare against. May be any valid commit, - not just an existing branch name. + not just an existing branch name. Defaults to the configured + upstream for the current branch. <branch>:: Working branch; defaults to HEAD. diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index ee14f74fd3..5a168cfab2 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -348,10 +348,12 @@ sendemail.confirm:: one of 'always', 'never', 'cc', 'compose', or 'auto'. See '--confirm' in the previous section for the meaning of these values. +EXAMPLE +------- Use gmail as the smtp server ----------------------------- - -Add the following section to the config file: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +To use 'git send-email' to send your patches through the GMail SMTP server, +edit ~/.gitconfig to specify your account settings: [sendemail] smtpencryption = tls @@ -359,9 +361,20 @@ Add the following section to the config file: smtpuser = yourname@gmail.com smtpserverport = 587 +Once your commits are ready to be sent to the mailing list, run the +following commands: + + $ git format-patch --cover-letter -M origin/master -o outgoing/ + $ edit outgoing/0000-* + $ git send-email outgoing/* + Note: the following perl modules are required Net::SMTP::SSL, MIME::Base64 and Authen::SASL +SEE ALSO +-------- +linkgit:git-format-patch[1], linkgit:git-imap-send[1], mbox(5) + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 1a16ff6044..5e7a4130ee 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -186,8 +186,10 @@ OPTIONS -f:: --force:: - This option is only valid for the add command. - Allow adding an otherwise ignored submodule path. + This option is only valid for add and update commands. + When running add, allow adding an otherwise ignored submodule path. + When running update, throw away local changes in submodules when + switching to a different commit. --cached:: This option is only valid for status and summary commands. These diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 71fc0ae8c5..713e523034 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -145,17 +145,6 @@ Skip "branches" and "tags" of first level directories;; ------------------------------------------------------------------------ -- ---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 - in the log message and use that as the author string. ---add-author-from;; - When committing to svn from git (as part of commit 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 - git commit's author string. If you use this, then --use-log-author - will retrieve a valid author string for all commits. - 'clone':: Runs 'init' and 'fetch'. It will automatically create a directory based on the basename of the URL passed to it; @@ -574,6 +563,17 @@ repository that will be fetched from. For 'branch' and 'tag', display the urls that will be used for copying when 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 + in the log message and use that as the author string. +--add-author-from:: + When committing to svn from git (as part of 'commit-diff', '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 + git commit's author string. If you use this, then `--use-log-author` + will retrieve a valid author string for all commits. + ADVANCED OPTIONS ---------------- @@ -784,10 +784,9 @@ use `git svn rebase` to update your work branch instead of `git pull` or when committing into SVN, which can lead to merge commits reversing previous commits in SVN. -DESIGN PHILOSOPHY ------------------ -Merge tracking in Subversion is lacking and doing branched development -with Subversion can be cumbersome as a result. While 'git svn' can track +MERGE TRACKING +-------------- +While 'git svn' can track copy history (including branches and tags) for repositories adopting a standard layout, it cannot yet represent merge history that happened inside git back upstream to SVN users. Therefore it is advised that @@ -797,16 +796,15 @@ compatibility with SVN (see the CAVEATS section below). CAVEATS ------- -For the sake of simplicity and interoperating with a less-capable system -(SVN), it is recommended that all 'git svn' users clone, fetch and dcommit +For the sake of simplicity and interoperating with Subversion, +it is recommended that all 'git svn' users clone, fetch and dcommit directly from the SVN server, and avoid all 'git clone'/'pull'/'merge'/'push' operations between git repositories and branches. The recommended method of exchanging code between git branches and users is 'git format-patch' and 'git am', or just 'dcommit'ing to the SVN repository. Running 'git merge' or 'git pull' is NOT recommended on a branch you -plan to 'dcommit' from. Subversion does not represent merges in any -reasonable or useful fashion; so users using Subversion cannot see any +plan to 'dcommit' from because Subversion users cannot see any merges you've made. Furthermore, if you merge or pull from a git branch that is a mirror of an SVN branch, 'dcommit' may commit to the wrong branch. @@ -856,7 +854,7 @@ Renamed and copied directories are not detected by git and hence not tracked when committing to SVN. I do not plan on adding support for this as it's quite difficult and time-consuming to get working for all the possible corner cases (git doesn't do it, either). Committing -renamed and copied files are fully supported if they're similar enough +renamed and copied files is fully supported if they're similar enough for git to detect them. CONFIGURATION diff --git a/Documentation/git.txt b/Documentation/git.txt index 9d5949229a..78420b133a 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -44,9 +44,10 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.7.5/git.html[documentation for release 1.7.5] +* link:v1.7.5.1/git.html[documentation for release 1.7.5.1] * release notes for + link:RelNotes/1.7.5.1.txt[1.7.5.1], link:RelNotes/1.7.5.txt[1.7.5]. * link:v1.7.4.5/git.html[documentation for release 1.7.4.5] diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt index 50923e2ce9..d5c977262a 100644 --- a/Documentation/pretty-options.txt +++ b/Documentation/pretty-options.txt @@ -30,19 +30,34 @@ people using 80-column terminals. preferred by the user. For non plumbing commands this defaults to UTF-8. ---no-notes:: ---show-notes[=<ref>]:: +--notes[=<ref>]:: Show the notes (see linkgit:git-notes[1]) that annotate the commit, when showing the commit log message. This is the default for `git log`, `git show` and `git whatchanged` commands when - there is no `--pretty`, `--format` nor `--oneline` option is - given on the command line. + there is no `--pretty`, `--format` nor `--oneline` option given + on the command line. ++ +By default, the notes shown are from the notes refs listed in the +'core.notesRef' and 'notes.displayRef' variables (or corresponding +environment overrides). See linkgit:git-config[1] for more details. ++ +With an optional '<ref>' argument, show this notes ref instead of the +default notes ref(s). The ref is taken to be in `refs/notes/` if it +is not qualified. + -With an optional argument, add this ref to the list of notes. The ref -is taken to be in `refs/notes/` if it is not qualified. +Multiple --notes options can be combined to control which notes are +being displayed. Examples: "--notes=foo" will show only notes from +"refs/notes/foo"; "--notes=foo --notes" will show both notes from +"refs/notes/foo" and from the default notes ref(s). +--no-notes:: + Do not show notes. This negates the above `--notes` option, by + resetting the list of notes refs from which notes are shown. + Options are parsed in the order given on the command line, so e.g. + "--notes --notes=foo --no-notes --notes=bar" will only show notes + from "refs/notes/bar". + +--show-notes[=<ref>]:: --[no-]standard-notes:: - Enable or disable populating the notes ref list from the - 'core.notesRef' and 'notes.displayRef' variables (or - corresponding environment overrides). Enabled by default. - See linkgit:git-config[1]. + These options are deprecated. Use the above --notes/--no-notes + options instead. diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 73111bb051..52bae31fcb 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -730,7 +730,10 @@ ifdef::git-rev-list[] Print a number stating how many commits would have been listed, and suppress all other output. When used together with '--left-right', instead print the counts for left and - right commits, separated by a tab. + right commits, separated by a tab. When used together with + '--cherry-mark', omit patch equivalent commits from these + counts and print the count for equivalent commits separated + by a tab. endif::git-rev-list[] @@ -368,7 +368,6 @@ SCRIPT_SH += git-merge-resolve.sh SCRIPT_SH += git-mergetool.sh SCRIPT_SH += git-pull.sh SCRIPT_SH += git-quiltimport.sh -SCRIPT_SH += git-rebase--interactive.sh SCRIPT_SH += git-rebase.sh SCRIPT_SH += git-repack.sh SCRIPT_SH += git-request-pull.sh @@ -378,6 +377,9 @@ SCRIPT_SH += git-web--browse.sh SCRIPT_LIB += git-mergetool--lib SCRIPT_LIB += git-parse-remote +SCRIPT_LIB += git-rebase--am +SCRIPT_LIB += git-rebase--interactive +SCRIPT_LIB += git-rebase--merge SCRIPT_LIB += git-sh-setup SCRIPT_PERL += git-add--interactive.perl @@ -157,6 +157,7 @@ int write_archive_entries(struct archiver_args *args, struct archiver_context context; struct unpack_trees_options opts; struct tree_desc t; + struct pathspec pathspec; int err; if (args->baselen > 0 && args->base[args->baselen - 1] == '/') { @@ -191,8 +192,10 @@ int write_archive_entries(struct archiver_args *args, git_attr_set_direction(GIT_ATTR_INDEX, &the_index); } - err = read_tree_recursive(args->tree, "", 0, 0, args->pathspec, + init_pathspec(&pathspec, args->pathspec); + err = read_tree_recursive(args->tree, "", 0, 0, &pathspec, write_archive_entry, &context); + free_pathspec(&pathspec); if (err == READ_TREE_RECURSIVE) err = 0; return err; @@ -221,11 +224,14 @@ static int reject_entry(const unsigned char *sha1, const char *base, static int path_exists(struct tree *tree, const char *path) { - const char *pathspec[] = { path, NULL }; - - if (read_tree_recursive(tree, "", 0, 0, pathspec, reject_entry, NULL)) - return 1; - return 0; + const char *paths[] = { path, NULL }; + struct pathspec pathspec; + int ret; + + init_pathspec(&pathspec, paths); + ret = read_tree_recursive(tree, "", 0, 0, &pathspec, reject_entry, NULL); + free_pathspec(&pathspec); + return ret != 0; } static void parse_pathspec_arg(const char **pathspec, diff --git a/builtin/blame.c b/builtin/blame.c index 4639788178..4242e4b513 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -1378,7 +1378,7 @@ static void get_ac_line(const char *inbuf, const char *what, timepos = tmp; *tmp = 0; - while (person < tmp && *tmp != ' ') + while (person < tmp && !(*tmp == ' ' && tmp[1] == '<')) tmp--; if (tmp <= person) return; diff --git a/builtin/checkout.c b/builtin/checkout.c index eece5d6ac0..4761769512 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -79,7 +79,10 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen, static int read_tree_some(struct tree *tree, const char **pathspec) { - read_tree_recursive(tree, "", 0, 0, pathspec, update_some, NULL); + struct pathspec ps; + init_pathspec(&ps, pathspec); + read_tree_recursive(tree, "", 0, 0, &ps, update_some, NULL); + free_pathspec(&ps); /* update the index with the given tree's info * for all args, expanding wildcards, and exit @@ -648,18 +651,30 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs) if (more == 1) describe_one_orphan(&sb, last); else - strbuf_addf(&sb, " ... and %d more.\n", more); + strbuf_addf(&sb, _(" ... and %d more.\n"), more); } fprintf(stderr, - "Warning: you are leaving %d commit%s behind, " + Q_( + /* The singular version */ + "Warning: you are leaving %d commit behind, " + "not connected to\n" + "any of your branches:\n\n" + "%s\n" + "If you want to keep it by creating a new branch, " + "this may be a good time\nto do so with:\n\n" + " git branch new_branch_name %s\n\n", + /* The plural version */ + "Warning: you are leaving %d commits behind, " "not connected to\n" "any of your branches:\n\n" "%s\n" "If you want to keep them by creating a new branch, " "this may be a good time\nto do so with:\n\n" " git branch new_branch_name %s\n\n", - lost, ((1 < lost) ? "s" : ""), + /* Give ngettext() the count */ + lost), + lost, sb.buf, sha1_to_hex(commit->object.sha1)); strbuf_release(&sb); @@ -961,9 +976,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) die (_("--patch is incompatible with all other options")); if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch)) - die("--detach cannot be used with -b/-B/--orphan"); + die(_("--detach cannot be used with -b/-B/--orphan")); if (opts.force_detach && 0 < opts.track) - die("--detach cannot be used with -t"); + die(_("--detach cannot be used with -t")); /* --track without -b should DWIM */ if (0 < opts.track && !opts.new_branch) { @@ -1043,7 +1058,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) } if (opts.force_detach) - die("git checkout: --detach does not take a path argument"); + die(_("git checkout: --detach does not take a path argument")); if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge) die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.")); diff --git a/builtin/clone.c b/builtin/clone.c index 4144bcf5ca..49c838fd3f 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -417,7 +417,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (path) repo = xstrdup(absolute_path(repo_name)); else if (!strchr(repo_name, ':')) - die("repository '%s' does not exist", repo_name); + die(_("repository '%s' does not exist"), repo_name); else repo = repo_name; is_local = path && !is_bundle; diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index 0d2a3e9fa2..be6417d166 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -163,6 +163,9 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) } if (read_stdin) { + int saved_nrl = 0; + int saved_dcctc = 0; + if (opt->diffopt.detect_rename) opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE | DIFF_SETUP_USE_CACHE); @@ -173,9 +176,16 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) fputs(line, stdout); fflush(stdout); } - else + else { diff_tree_stdin(line); + if (saved_nrl < opt->diffopt.needed_rename_limit) + saved_nrl = opt->diffopt.needed_rename_limit; + if (opt->diffopt.degraded_cc_to_c) + saved_dcctc = 1; + } } + opt->diffopt.degraded_cc_to_c = saved_dcctc; + opt->diffopt.needed_rename_limit = saved_nrl; } return diff_result_code(&opt->diffopt, 0); diff --git a/builtin/diff.c b/builtin/diff.c index 717fa1a341..14bd14fce0 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -202,7 +202,6 @@ static void refresh_index_quietly(void) static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv) { - int result; unsigned int options = 0; while (1 < argc && argv[1][0] == '-') { @@ -236,8 +235,7 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv perror("read_cache_preload"); return -1; } - result = run_diff_files(revs, options); - return diff_result_code(&revs->diffopt, result); + return run_diff_files(revs, options); } int cmd_diff(int argc, const char **argv, const char *prefix) diff --git a/builtin/grep.c b/builtin/grep.c index 10a1f65310..3ee2ec51de 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -533,18 +533,18 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, struct tree_desc *tree, struct strbuf *base, int tn_len) { - int hit = 0, matched = 0; + int hit = 0, match = 0; struct name_entry entry; int old_baselen = base->len; while (tree_entry(tree, &entry)) { int te_len = tree_entry_len(entry.path, entry.sha1); - if (matched != 2) { - matched = tree_entry_interesting(&entry, base, tn_len, pathspec); - if (matched == -1) - break; /* no more matches */ - if (!matched) + if (match != 2) { + match = tree_entry_interesting(&entry, base, tn_len, pathspec); + if (match < 0) + break; + if (match == 0) continue; } diff --git a/builtin/init-db.c b/builtin/init-db.c index b7370d9bb8..ba13a54793 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -319,10 +319,10 @@ int set_git_dir_init(const char *git_dir, const char *real_git_dir, struct stat st; if (!exist_ok && !stat(git_dir, &st)) - die("%s already exists", git_dir); + die(_("%s already exists"), git_dir); if (!exist_ok && !stat(real_git_dir, &st)) - die("%s already exists", real_git_dir); + die(_("%s already exists"), real_git_dir); /* * make sure symlinks are resolved because we'll be @@ -351,15 +351,15 @@ static void separate_git_dir(const char *git_dir) else if (S_ISDIR(st.st_mode)) src = git_link; else - die("unable to handle file type %d", st.st_mode); + die(_("unable to handle file type %d"), st.st_mode); if (rename(src, git_dir)) - die_errno("unable to move %s to %s", src, git_dir); + die_errno(_("unable to move %s to %s"), src, git_dir); } fp = fopen(git_link, "w"); if (!fp) - die("Could not create git link %s", git_link); + die(_("Could not create git link %s"), git_link); fprintf(fp, "gitdir: %s\n", git_dir); fclose(fp); } diff --git a/builtin/log.c b/builtin/log.c index c6dce9b895..d43ad3a617 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -256,6 +256,8 @@ static void finish_early_output(struct rev_info *rev) static int cmd_log_walk(struct rev_info *rev) { struct commit *commit; + int saved_nrl = 0; + int saved_dcctc = 0; if (rev->early_output) setup_early_output(rev); @@ -286,7 +288,14 @@ static int cmd_log_walk(struct rev_info *rev) } free_commit_list(commit->parents); commit->parents = NULL; + if (saved_nrl < rev->diffopt.needed_rename_limit) + saved_nrl = rev->diffopt.needed_rename_limit; + if (rev->diffopt.degraded_cc_to_c) + saved_dcctc = 1; } + rev->diffopt.degraded_cc_to_c = saved_dcctc; + rev->diffopt.needed_rename_limit = saved_nrl; + if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF && DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) { return 02; @@ -405,6 +414,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) struct rev_info rev; struct object_array_entry *objects; struct setup_revision_opt opt; + struct pathspec match_all; int i, count, ret = 0; git_config(git_log_config, NULL); @@ -412,6 +422,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; + init_pathspec(&match_all, NULL); init_revisions(&rev, prefix); rev.diff = 1; rev.always_show_header = 1; @@ -458,7 +469,7 @@ int cmd_show(int argc, const char **argv, const char *prefix) diff_get_color_opt(&rev.diffopt, DIFF_COMMIT), name, diff_get_color_opt(&rev.diffopt, DIFF_RESET)); - read_tree_recursive((struct tree *)o, "", 0, 0, NULL, + read_tree_recursive((struct tree *)o, "", 0, 0, &match_all, show_tree_object, NULL); rev.shown_one = 1; break; diff --git a/builtin/ls-files.c b/builtin/ls-files.c index fb2d5f4b1f..15701233e2 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -338,7 +338,7 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix) { struct tree *tree; unsigned char sha1[20]; - const char **match; + struct pathspec pathspec; struct cache_entry *last_stage0 = NULL; int i; @@ -360,10 +360,11 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix) static const char *(matchbuf[2]); matchbuf[0] = prefix; matchbuf[1] = NULL; - match = matchbuf; + init_pathspec(&pathspec, matchbuf); + pathspec.items[0].use_wildcard = 0; } else - match = NULL; - if (read_tree(tree, 1, match)) + init_pathspec(&pathspec, NULL); + if (read_tree(tree, 1, &pathspec)) die("unable to read tree entries %s", tree_name); for (i = 0; i < active_nr; i++) { diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index f73e6bd962..f08c5b0c94 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -19,7 +19,7 @@ static int line_termination = '\n'; #define LS_SHOW_SIZE 16 static int abbrev; static int ls_options; -static const char **pathspec; +static struct pathspec pathspec; static int chomp_prefix; static const char *ls_tree_prefix; @@ -35,7 +35,7 @@ static int show_recursive(const char *base, int baselen, const char *pathname) if (ls_options & LS_RECURSIVE) return 1; - s = pathspec; + s = pathspec.raw; if (!s) return 0; @@ -120,7 +120,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) { unsigned char sha1[20]; struct tree *tree; - int full_tree = 0; + int i, full_tree = 0; const struct option ls_tree_options[] = { OPT_BIT('d', NULL, &ls_options, "only show trees", LS_TREE_ONLY), @@ -166,11 +166,14 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) if (get_sha1(argv[0], sha1)) die("Not a valid object name %s", argv[0]); - pathspec = get_pathspec(prefix, argv + 1); + init_pathspec(&pathspec, get_pathspec(prefix, argv + 1)); + for (i = 0; i < pathspec.nr; i++) + pathspec.items[i].use_wildcard = 0; + pathspec.has_wildcard = 0; tree = parse_tree_indirect(sha1); if (!tree) die("not a tree object"); - read_tree_recursive(tree, "", 0, 0, pathspec, show_tree, NULL); + read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL); return 0; } diff --git a/builtin/merge-base.c b/builtin/merge-base.c index 96dd160731..4f30f1b0c8 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -23,7 +23,8 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) } static const char * const merge_base_usage[] = { - "git merge-base [-a|--all] [--octopus] <commit> <commit>...", + "git merge-base [-a|--all] <commit> <commit>...", + "git merge-base [-a|--all] --octopus <commit>...", "git merge-base --independent <commit>...", NULL }; diff --git a/builtin/merge.c b/builtin/merge.c index 0bdd19a137..d171c63c52 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -822,7 +822,7 @@ static void read_merge_msg(void) { strbuf_reset(&merge_msg); if (strbuf_read_file(&merge_msg, git_path("MERGE_MSG"), 0) < 0) - die_errno("Could not read from '%s'", git_path("MERGE_MSG")); + die_errno(_("Could not read from '%s'"), git_path("MERGE_MSG")); } static void run_prepare_commit_msg(void) @@ -962,16 +962,16 @@ static int setup_with_upstream(const char ***argv) const char **args; if (!branch) - die("No current branch."); + die(_("No current branch.")); if (!branch->remote) - die("No remote for the current branch."); + die(_("No remote for the current branch.")); if (!branch->merge_nr) - die("No default upstream defined for the current branch."); + die(_("No default upstream defined for the current branch.")); args = xcalloc(branch->merge_nr + 1, sizeof(char *)); for (i = 0; i < branch->merge_nr; i++) { if (!branch->merge[i]->dst) - die("No remote tracking branch for %s from %s", + die(_("No remote tracking branch for %s from %s"), branch->merge[i]->src, branch->remote_name); args[i] = branch->merge[i]->dst; } @@ -1043,10 +1043,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix) } if (file_exists(git_path("CHERRY_PICK_HEAD"))) { if (advice_resolve_conflict) - die("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n" - "Please, commit your changes before you can merge."); + die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n" + "Please, commit your changes before you can merge.")); else - die("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."); + die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).")); } resolve_undo_clear(); diff --git a/builtin/notes.c b/builtin/notes.c index d6dcfcb014..1fb1f73439 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -100,16 +100,6 @@ struct msg_arg { struct strbuf buf; }; -static void expand_notes_ref(struct strbuf *sb) -{ - if (!prefixcmp(sb->buf, "refs/notes/")) - return; /* we're happy */ - else if (!prefixcmp(sb->buf, "notes/")) - strbuf_insert(sb, 0, "refs/", 5); - else - strbuf_insert(sb, 0, "refs/notes/", 11); -} - static int list_each_note(const unsigned char *object_sha1, const unsigned char *note_sha1, char *note_path, void *cb_data) @@ -529,6 +519,8 @@ static int list(int argc, const char **argv, const char *prefix) return retval; } +static int append_edit(int argc, const char **argv, const char *prefix); + static int add(int argc, const char **argv, const char *prefix) { int retval = 0, force = 0; @@ -556,14 +548,14 @@ static int add(int argc, const char **argv, const char *prefix) }; argc = parse_options(argc, argv, prefix, options, git_notes_add_usage, - 0); + PARSE_OPT_KEEP_ARGV0); - if (1 < argc) { + if (2 < argc) { error(_("too many parameters")); usage_with_options(git_notes_add_usage, options); } - object_ref = argc ? argv[0] : "HEAD"; + object_ref = argc > 1 ? argv[1] : "HEAD"; if (get_sha1(object_ref, object)) die(_("Failed to resolve '%s' as a valid ref."), object_ref); @@ -573,6 +565,18 @@ static int add(int argc, const char **argv, const char *prefix) if (note) { if (!force) { + if (!msg.given) { + /* + * Redirect to "edit" subcommand. + * + * We only end up here if none of -m/-F/-c/-C + * or -f are given. The original args are + * therefore still in argv[0-1]. + */ + argv[0] = "edit"; + free_notes(t); + return append_edit(argc, argv, prefix); + } retval = error(_("Cannot add notes. Found existing notes " "for object %s. Use '-f' to overwrite " "existing notes"), sha1_to_hex(object)); diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 9bfb94201f..4be66998f6 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -55,7 +55,9 @@ static void show_commit(struct commit *commit, void *data) graph_show_commit(revs->graph); if (revs->count) { - if (commit->object.flags & SYMMETRIC_LEFT) + if (commit->object.flags & PATCHSAME) + revs->count_same++; + else if (commit->object.flags & SYMMETRIC_LEFT) revs->count_left++; else revs->count_right++; @@ -406,8 +408,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) &info); if (revs.count) { - if (revs.left_right) + if (revs.left_right && revs.cherry_mark) + printf("%d\t%d\t%d\n", revs.count_left, revs.count_right, revs.count_same); + else if (revs.left_right) printf("%d\t%d\n", revs.count_left, revs.count_right); + else if (revs.cherry_mark) + printf("%d\t%d\n", revs.count_left + revs.count_right, revs.count_same); else printf("%d\n", revs.count_left + revs.count_right); } @@ -511,7 +511,7 @@ struct pathspec { struct pathspec_item { const char *match; int len; - unsigned int has_wildcard:1; + unsigned int use_wildcard:1; } *items; }; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 00691fcd82..b81f444496 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1534,7 +1534,7 @@ __git_log_common_options=" __git_log_gitk_options=" --dense --sparse --full-history --simplify-merges --simplify-by-decoration - --left-right + --left-right --notes --no-notes " # Options that go well for log and shortlog (not gitk) __git_log_shortlog_options=" @@ -581,11 +581,14 @@ static void emit_rewrite_diff(const char *name_a, line_prefix, metainfo, a_name.buf, name_a_tab, reset, line_prefix, metainfo, b_name.buf, name_b_tab, reset, line_prefix, fraginfo); - print_line_count(o->file, lc_a); + if (!o->irreversible_delete) + print_line_count(o->file, lc_a); + else + fprintf(o->file, "?,?"); fprintf(o->file, " +"); print_line_count(o->file, lc_b); fprintf(o->file, " @@%s\n", reset); - if (lc_a) + if (lc_a && !o->irreversible_delete) emit_rewrite_lines(&ecbdata, '-', data_one, size_one); if (lc_b) emit_rewrite_lines(&ecbdata, '+', data_two, size_two); @@ -1538,8 +1541,36 @@ static void show_dirstat(struct diff_options *options) struct diff_filepair *p = q->queue[i]; const char *name; unsigned long copied, added, damage; + int content_changed; + + name = p->two->path ? p->two->path : p->one->path; + + if (p->one->sha1_valid && p->two->sha1_valid) + content_changed = hashcmp(p->one->sha1, p->two->sha1); + else + content_changed = 1; + + if (!content_changed) { + /* + * The SHA1 has not changed, so pre-/post-content is + * identical. We can therefore skip looking at the + * file contents altogether. + */ + damage = 0; + goto found_damage; + } - name = p->one->path ? p->one->path : p->two->path; + if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE)) { + /* + * In --dirstat-by-file mode, we don't really need to + * look at the actual file contents at all. + * The fact that the SHA1 changed is enough for us to + * add this file to the list of results + * (with each file contributing equal damage). + */ + damage = 1; + goto found_damage; + } if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) { diff_populate_filespec(p->one, 0); @@ -1563,14 +1594,18 @@ static void show_dirstat(struct diff_options *options) /* * Original minus copied is the removed material, * added is the new material. They are both damages - * made to the preimage. In --dirstat-by-file mode, count - * damaged files, not damaged lines. This is done by - * counting only a single damaged line per file. + * made to the preimage. + * If the resulting damage is zero, we know that + * diffcore_count_changes() considers the two entries to + * be identical, but since content_changed is true, we + * know that there must have been _some_ kind of change, + * so we force all entries to have damage > 0. */ damage = (p->one->size - copied) + added; - if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE) && damage > 0) + if (!damage) damage = 1; +found_damage: ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc); dir.files[dir.nr].name = name; dir.files[dir.nr].changed = damage; @@ -1949,7 +1984,11 @@ static void builtin_diff(const char *name_a, } } - if (!DIFF_OPT_TST(o, TEXT) && + if (o->irreversible_delete && lbl[1][0] == '/') { + fprintf(o->file, "%s", header.buf); + strbuf_reset(&header); + goto free_ab_and_return; + } else if (!DIFF_OPT_TST(o, TEXT) && ( (!textconv_one && diff_filespec_is_binary(one)) || (!textconv_two && diff_filespec_is_binary(two)) )) { if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) @@ -1969,8 +2008,7 @@ static void builtin_diff(const char *name_a, fprintf(o->file, "%sBinary files %s and %s differ\n", line_prefix, lbl[0], lbl[1]); o->found_changes = 1; - } - else { + } else { /* Crazy xdl interfaces.. */ const char *diffopts = getenv("GIT_DIFF_OPTS"); xpparam_t xpp; @@ -3168,6 +3206,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) return error("invalid argument to -M: %s", arg+2); options->detect_rename = DIFF_DETECT_RENAME; } + else if (!strcmp(arg, "-D") || !strcmp(arg, "--irreversible-delete")) { + options->irreversible_delete = 1; + } else if (!prefixcmp(arg, "-C") || !prefixcmp(arg, "--find-copies=") || !strcmp(arg, "--find-copies")) { if (options->detect_rename == DIFF_DETECT_COPY) @@ -3955,6 +3996,28 @@ static int is_summary_empty(const struct diff_queue_struct *q) return 1; } +static const char rename_limit_warning[] = +"inexact rename detection was skipped due to too many files."; + +static const char degrade_cc_to_c_warning[] = +"only found copies from modified paths due to too many files."; + +static const char rename_limit_advice[] = +"you may want to set your %s variable to at least " +"%d and retry the command."; + +void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc) +{ + if (degraded_cc) + warning(degrade_cc_to_c_warning); + else if (needed) + warning(rename_limit_warning); + else + return; + if (0 < needed && needed < 32767) + warning(rename_limit_advice, varname, needed); +} + void diff_flush(struct diff_options *options) { struct diff_queue_struct *q = &diff_queued_diff; @@ -4236,6 +4299,10 @@ void diffcore_std(struct diff_options *options) int diff_result_code(struct diff_options *opt, int status) { int result = 0; + + diff_warn_rename_limit("diff.renamelimit", + opt->needed_rename_limit, + opt->degraded_cc_to_c); if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) && !(opt->output_format & DIFF_FORMAT_CHECKDIFF)) return status; @@ -104,6 +104,7 @@ struct diff_options { int interhunkcontext; int break_opt; int detect_rename; + int irreversible_delete; int skip_stat_unmatch; int line_termination; int output_format; @@ -111,6 +112,7 @@ struct diff_options { int rename_score; int rename_limit; int needed_rename_limit; + int degraded_cc_to_c; int show_rename_progress; int dirstat_percent; int setup; @@ -273,6 +275,7 @@ extern void diffcore_fix_diff_index(struct diff_options *); extern int diff_queue_is_empty(void); extern void diff_flush(struct diff_options*); +extern void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc); /* diff-raw status letters */ #define DIFF_STATUS_ADDED 'A' diff --git a/diffcore-rename.c b/diffcore-rename.c index d40e40a3ac..f639601c76 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -55,22 +55,23 @@ static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two, /* Table of rename/copy src files */ static struct diff_rename_src { - struct diff_filespec *one; + struct diff_filepair *p; unsigned short score; /* to remember the break score */ } *rename_src; static int rename_src_nr, rename_src_alloc; -static struct diff_rename_src *register_rename_src(struct diff_filespec *one, - unsigned short score) +static struct diff_rename_src *register_rename_src(struct diff_filepair *p) { int first, last; + struct diff_filespec *one = p->one; + unsigned short score = p->score; first = 0; last = rename_src_nr; while (last > first) { int next = (last + first) >> 1; struct diff_rename_src *src = &(rename_src[next]); - int cmp = strcmp(one->path, src->one->path); + int cmp = strcmp(one->path, src->p->one->path); if (!cmp) return src; if (cmp < 0) { @@ -90,7 +91,7 @@ static struct diff_rename_src *register_rename_src(struct diff_filespec *one, if (first < rename_src_nr) memmove(rename_src + first + 1, rename_src + first, (rename_src_nr - first - 1) * sizeof(*rename_src)); - rename_src[first].one = one; + rename_src[first].p = p; rename_src[first].score = score; return &(rename_src[first]); } @@ -205,7 +206,7 @@ static void record_rename_pair(int dst_index, int src_index, int score) if (rename_dst[dst_index].pair) die("internal error: dst already matched."); - src = rename_src[src_index].one; + src = rename_src[src_index].p->one; src->rename_used++; src->count++; @@ -389,7 +390,7 @@ static int find_exact_renames(struct diff_options *options) init_hash(&file_table); for (i = 0; i < rename_src_nr; i++) - insert_file_table(&file_table, -1, i, rename_src[i].one); + insert_file_table(&file_table, -1, i, rename_src[i].p->one); for (i = 0; i < rename_dst_nr; i++) insert_file_table(&file_table, 1, i, rename_dst[i].two); @@ -419,6 +420,55 @@ static void record_if_better(struct diff_score m[], struct diff_score *o) m[worst] = *o; } +/* + * Returns: + * 0 if we are under the limit; + * 1 if we need to disable inexact rename detection; + * 2 if we would be under the limit if we were given -C instead of -C -C. + */ +static int too_many_rename_candidates(int num_create, + struct diff_options *options) +{ + int rename_limit = options->rename_limit; + int num_src = rename_src_nr; + int i; + + options->needed_rename_limit = 0; + + /* + * This basically does a test for the rename matrix not + * growing larger than a "rename_limit" square matrix, ie: + * + * num_create * num_src > rename_limit * rename_limit + * + * but handles the potential overflow case specially (and we + * assume at least 32-bit integers) + */ + if (rename_limit <= 0 || rename_limit > 32767) + rename_limit = 32767; + if ((num_create <= rename_limit || num_src <= rename_limit) && + (num_create * num_src <= rename_limit * rename_limit)) + return 0; + + options->needed_rename_limit = + num_src > num_create ? num_src : num_create; + + /* Are we running under -C -C? */ + if (!DIFF_OPT_TST(options, FIND_COPIES_HARDER)) + return 1; + + /* Would we bust the limit if we were running under -C? */ + for (num_src = i = 0; i < rename_src_nr; i++) { + if (diff_unmodified_pair(rename_src[i].p)) + continue; + num_src++; + } + if ((num_create <= rename_limit || num_src <= rename_limit) && + (num_create * num_src <= rename_limit * rename_limit)) + return 2; + return 1; +} + static int find_renames(struct diff_score *mx, int dst_cnt, int minimum_score, int copies) { int count = 0, i; @@ -432,7 +482,7 @@ static int find_renames(struct diff_score *mx, int dst_cnt, int minimum_score, i dst = &rename_dst[mx[i].dst]; if (dst->pair) continue; /* already done, either exact or fuzzy. */ - if (!copies && rename_src[mx[i].src].one->rename_used) + if (!copies && rename_src[mx[i].src].p->one->rename_used) continue; record_rename_pair(mx[i].dst, mx[i].src, mx[i].score); count++; @@ -444,12 +494,11 @@ void diffcore_rename(struct diff_options *options) { int detect_rename = options->detect_rename; int minimum_score = options->rename_score; - int rename_limit = options->rename_limit; struct diff_queue_struct *q = &diff_queued_diff; struct diff_queue_struct outq; struct diff_score *mx; - int i, j, rename_count; - int num_create, num_src, dst_cnt; + int i, j, rename_count, skip_unmodified = 0; + int num_create, dst_cnt; struct progress *progress = NULL; if (!minimum_score) @@ -466,7 +515,7 @@ void diffcore_rename(struct diff_options *options) else locate_rename_dst(p->two, 1); } - else if (!DIFF_FILE_VALID(p->two)) { + else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) { /* * If the source is a broken "delete", and * they did not really want to get broken, @@ -476,7 +525,7 @@ void diffcore_rename(struct diff_options *options) */ if (p->broken_pair && !p->score) p->one->rename_used++; - register_rename_src(p->one, p->score); + register_rename_src(p); } else if (detect_rename == DIFF_DETECT_COPY) { /* @@ -484,7 +533,7 @@ void diffcore_rename(struct diff_options *options) * one, to indicate ourselves as a user. */ p->one->rename_used++; - register_rename_src(p->one, p->score); + register_rename_src(p); } } if (rename_dst_nr == 0 || rename_src_nr == 0) @@ -505,29 +554,20 @@ void diffcore_rename(struct diff_options *options) * files still remain as options for rename/copies!) */ num_create = (rename_dst_nr - rename_count); - num_src = rename_src_nr; /* All done? */ if (!num_create) goto cleanup; - /* - * This basically does a test for the rename matrix not - * growing larger than a "rename_limit" square matrix, ie: - * - * num_create * num_src > rename_limit * rename_limit - * - * but handles the potential overflow case specially (and we - * assume at least 32-bit integers) - */ - options->needed_rename_limit = 0; - if (rename_limit <= 0 || rename_limit > 32767) - rename_limit = 32767; - if ((num_create > rename_limit && num_src > rename_limit) || - (num_create * num_src > rename_limit * rename_limit)) { - options->needed_rename_limit = - num_src > num_create ? num_src : num_create; + switch (too_many_rename_candidates(num_create, options)) { + case 1: goto cleanup; + case 2: + options->degraded_cc_to_c = 1; + skip_unmodified = 1; + break; + default: + break; } if (options->show_rename_progress) { @@ -549,8 +589,13 @@ void diffcore_rename(struct diff_options *options) m[j].dst = -1; for (j = 0; j < rename_src_nr; j++) { - struct diff_filespec *one = rename_src[j].one; + struct diff_filespec *one = rename_src[j].p->one; struct diff_score this_src; + + if (skip_unmodified && + diff_unmodified_pair(rename_src[j].p)) + continue; + this_src.score = estimate_similarity(one, two, minimum_score); this_src.name_score = basename_same(one, two); @@ -586,7 +631,10 @@ void diffcore_rename(struct diff_options *options) struct diff_filepair *p = q->queue[i]; struct diff_filepair *pair_to_free = NULL; - if (!DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) { + if (DIFF_PAIR_UNMERGED(p)) { + diff_q(&outq, p); + } + else if (!DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) { /* * Creation * @@ -230,7 +230,7 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix, return MATCHED_RECURSIVELY; } - if (item->has_wildcard && !fnmatch(match, name, 0)) + if (item->use_wildcard && !fnmatch(match, name, 0)) return MATCHED_FNMATCH; return 0; @@ -1105,57 +1105,45 @@ int file_exists(const char *f) } /* - * get_relative_cwd() gets the prefix of the current working directory - * relative to 'dir'. If we are not inside 'dir', it returns NULL. - * - * As a convenience, it also returns NULL if 'dir' is already NULL. The - * reason for this behaviour is that it is natural for functions returning - * directory names to return NULL to say "this directory does not exist" - * or "this directory is invalid". These cases are usually handled the - * same as if the cwd is not inside 'dir' at all, so get_relative_cwd() - * returns NULL for both of them. - * - * Most notably, get_relative_cwd(buffer, size, get_git_work_tree()) - * unifies the handling of "outside work tree" with "no work tree at all". + * Given two normalized paths (a trailing slash is ok), if subdir is + * outside dir, return -1. Otherwise return the offset in subdir that + * can be used as relative path to dir. */ -char *get_relative_cwd(char *buffer, int size, const char *dir) +int dir_inside_of(const char *subdir, const char *dir) { - char *cwd = buffer; - - if (!dir) - return NULL; - if (!getcwd(buffer, size)) - die_errno("can't find the current directory"); + int offset = 0; - if (!is_absolute_path(dir)) - dir = real_path(dir); + assert(dir && subdir && *dir && *subdir); - while (*dir && *dir == *cwd) { + while (*dir && *subdir && *dir == *subdir) { dir++; - cwd++; - } - if (*dir) - return NULL; - switch (*cwd) { - case '\0': - return cwd; - case '/': - return cwd + 1; - default: - /* - * dir can end with a path separator when it's root - * directory. Return proper prefix in that case. - */ - if (dir[-1] == '/') - return cwd; - return NULL; + subdir++; + offset++; } + + /* hel[p]/me vs hel[l]/yeah */ + if (*dir && *subdir) + return -1; + + if (!*subdir) + return !*dir ? offset : -1; /* same dir */ + + /* foo/[b]ar vs foo/[] */ + if (is_dir_sep(dir[-1])) + return is_dir_sep(subdir[-1]) ? offset : -1; + + /* foo[/]bar vs foo[] */ + return is_dir_sep(*subdir) ? offset + 1 : -1; } int is_inside_dir(const char *dir) { - char buffer[PATH_MAX]; - return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL; + char cwd[PATH_MAX]; + if (!dir) + return 0; + if (!getcwd(cwd, sizeof(cwd))) + die_errno("can't find the current directory"); + return dir_inside_of(cwd, dir) >= 0; } int is_empty_dir(const char *path) @@ -1286,8 +1274,8 @@ int init_pathspec(struct pathspec *pathspec, const char **paths) item->match = path; item->len = strlen(path); - item->has_wildcard = !no_wildcard(path); - if (item->has_wildcard) + item->use_wildcard = !no_wildcard(path); + if (item->use_wildcard) pathspec->has_wildcard = 1; } @@ -85,8 +85,8 @@ extern void add_exclude(const char *string, const char *base, extern void free_excludes(struct exclude_list *el); extern int file_exists(const char *); -extern char *get_relative_cwd(char *buffer, int size, const char *dir); extern int is_inside_dir(const char *dir); +extern int dir_inside_of(const char *subdir, const char *dir); static inline int is_dot_or_dotdot(const char *name) { diff --git a/git-mergetool.sh b/git-mergetool.sh index bacbda2bb7..3aab5aae84 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -21,6 +21,10 @@ is_symlink () { test "$1" = 120000 } +is_submodule () { + test "$1" = 160000 +} + local_present () { test -n "$local_mode" } @@ -35,7 +39,8 @@ base_present () { cleanup_temp_files () { if test "$1" = --save-backup ; then - mv -- "$BACKUP" "$MERGED.orig" + rm -rf -- "$MERGED.orig" + test -e "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig" rm -f -- "$LOCAL" "$REMOTE" "$BASE" else rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP" @@ -52,11 +57,13 @@ describe_file () { echo "deleted" elif is_symlink "$mode" ; then echo "a symbolic link -> '$(cat "$file")'" + elif is_submodule "$mode" ; then + echo "submodule commit $file" else if base_present; then - echo "modified" + echo "modified file" else - echo "created" + echo "created file" fi fi } @@ -112,6 +119,67 @@ resolve_deleted_merge () { done } +resolve_submodule_merge () { + while true; do + printf "Use (l)ocal or (r)emote, or (a)bort? " + read ans + case "$ans" in + [lL]*) + if ! local_present; then + if test -n "$(git ls-tree HEAD -- "$MERGED")"; then + # Local isn't present, but it's a subdirectory + git ls-tree --full-name -r HEAD -- "$MERGED" | git update-index --index-info || exit $? + else + test -e "$MERGED" && mv -- "$MERGED" "$BACKUP" + git update-index --force-remove "$MERGED" + cleanup_temp_files --save-backup + fi + elif is_submodule "$local_mode"; then + stage_submodule "$MERGED" "$local_sha1" + else + git checkout-index -f --stage=2 -- "$MERGED" + git add -- "$MERGED" + fi + return 0 + ;; + [rR]*) + if ! remote_present; then + if test -n "$(git ls-tree MERGE_HEAD -- "$MERGED")"; then + # Remote isn't present, but it's a subdirectory + git ls-tree --full-name -r MERGE_HEAD -- "$MERGED" | git update-index --index-info || exit $? + else + test -e "$MERGED" && mv -- "$MERGED" "$BACKUP" + git update-index --force-remove "$MERGED" + fi + elif is_submodule "$remote_mode"; then + ! is_submodule "$local_mode" && test -e "$MERGED" && mv -- "$MERGED" "$BACKUP" + stage_submodule "$MERGED" "$remote_sha1" + else + test -e "$MERGED" && mv -- "$MERGED" "$BACKUP" + git checkout-index -f --stage=3 -- "$MERGED" + git add -- "$MERGED" + fi + cleanup_temp_files --save-backup + return 0 + ;; + [aA]*) + return 1 + ;; + esac + done +} + +stage_submodule () { + path="$1" + submodule_sha1="$2" + mkdir -p "$path" || die "fatal: unable to create directory for module at $path" + # Find $path relative to work tree + work_tree_root=$(cd_to_toplevel && pwd) + work_rel_path=$(cd "$path" && GIT_WORK_TREE="${work_tree_root}" git rev-parse --show-prefix) + test -n "$work_rel_path" || die "fatal: unable to get path of module $path relative to work tree" + git update-index --add --replace --cacheinfo 160000 "$submodule_sha1" "${work_rel_path%/}" || die +} + checkout_staged_file () { tmpfile=$(expr "$(git checkout-index --temp --stage="$1" "$2")" : '\([^ ]*\) ') @@ -139,13 +207,23 @@ merge_file () { REMOTE="./$MERGED.REMOTE.$ext" BASE="./$MERGED.BASE.$ext" - mv -- "$MERGED" "$BACKUP" - cp -- "$BACKUP" "$MERGED" - base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}') local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}') remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}') + if is_submodule "$local_mode" || is_submodule "$remote_mode"; then + echo "Submodule merge conflict for '$MERGED':" + local_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $2;}') + remote_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $2;}') + describe_file "$local_mode" "local" "$local_sha1" + describe_file "$remote_mode" "remote" "$remote_sha1" + resolve_submodule_merge + return + fi + + mv -- "$MERGED" "$BACKUP" + cp -- "$BACKUP" "$MERGED" + base_present && checkout_staged_file 1 "$MERGED" "$BASE" local_present && checkout_staged_file 2 "$MERGED" "$LOCAL" remote_present && checkout_staged_file 3 "$MERGED" "$REMOTE" diff --git a/git-parse-remote.sh b/git-parse-remote.sh index ea093d251d..b24119d69c 100644 --- a/git-parse-remote.sh +++ b/git-parse-remote.sh @@ -50,3 +50,41 @@ get_remote_merge_branch () { esac esac } + +error_on_missing_default_upstream () { + cmd="$1" + op_type="$2" + op_prep="$3" + example="$4" + branch_name=$(git symbolic-ref -q HEAD) + if test -z "$branch_name" + then + echo "You are not currently on a branch, so I cannot use any +'branch.<branchname>.merge' in your configuration file. +Please specify which branch you want to $op_type $op_prep on the command +line and try again (e.g. '$example'). +See git-${cmd}(1) for details." + else + echo "You asked me to $cmd without telling me which branch you +want to $op_type $op_prep, and 'branch.${branch_name#refs/heads/}.merge' in +your configuration file does not tell me, either. Please +specify which branch you want to use on the command line and +try again (e.g. '$example'). +See git-${cmd}(1) for details. + +If you often $op_type $op_prep the same branch, you may want to +use something like the following in your configuration file: + [branch \"${branch_name#refs/heads/}\"] + remote = <nickname> + merge = <remote-ref>" + test rebase = "$op_type" && + echo " rebase = true" + echo " + [remote \"<nickname>\"] + url = <url> + fetch = <refspec> + +See git-config(1) for details." + fi + exit 1 +} diff --git a/git-pull.sh b/git-pull.sh index 4e9e0e49ec..fb9e2df931 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -168,34 +168,10 @@ error_on_no_merge_candidates () { echo "You asked to pull from the remote '$1', but did not specify" echo "a branch. Because this is not the default configured remote" echo "for your current branch, you must specify a branch on the command line." - elif [ -z "$curr_branch" ]; then - echo "You are not currently on a branch, so I cannot use any" - echo "'branch.<branchname>.merge' in your configuration file." - echo "Please specify which remote branch you want to use on the command" - echo "line and try again (e.g. 'git pull <repository> <refspec>')." - echo "See git-pull(1) for details." - elif [ -z "$upstream" ]; then - echo "You asked me to pull without telling me which branch you" - echo "want to $op_type $op_prep, and 'branch.${curr_branch}.merge' in" - echo "your configuration file does not tell me, either. Please" - echo "specify which branch you want to use on the command line and" - echo "try again (e.g. 'git pull <repository> <refspec>')." - echo "See git-pull(1) for details." - echo - echo "If you often $op_type $op_prep the same branch, you may want to" - echo "use something like the following in your configuration file:" - echo - echo " [branch \"${curr_branch}\"]" - echo " remote = <nickname>" - echo " merge = <remote-ref>" - test rebase = "$op_type" && - echo " rebase = true" - echo - echo " [remote \"<nickname>\"]" - echo " url = <url>" - echo " fetch = <refspec>" - echo - echo "See git-config(1) for details." + elif [ -z "$curr_branch" -o -z "$upstream" ]; then + . git-parse-remote + error_on_missing_default_upstream "pull" $op_type $op_prep \ + "git pull <repository> <refspec>" else echo "Your configuration specifies to $op_type $op_prep the ref '${upstream#refs/heads/}'" echo "from the remote, but no such ref was fetched." diff --git a/git-rebase--am.sh b/git-rebase--am.sh new file mode 100644 index 0000000000..c815a2412c --- /dev/null +++ b/git-rebase--am.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# +# Copyright (c) 2010 Junio C Hamano. +# + +. git-sh-setup + +case "$action" in +continue) + git am --resolved --resolvemsg="$resolvemsg" && + move_to_original_branch + exit + ;; +skip) + git am --skip --resolvemsg="$resolvemsg" && + move_to_original_branch + exit + ;; +esac + +test -n "$rebase_root" && root_flag=--root + +git format-patch -k --stdout --full-index --ignore-if-in-upstream \ + --src-prefix=a/ --dst-prefix=b/ \ + --no-renames $root_flag "$revisions" | +git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" && +move_to_original_branch +ret=$? +test 0 != $ret -a -d "$state_dir" && write_basic_state +exit $ret diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 5873ba4bc3..41ba96aeb7 100755..100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -10,48 +10,22 @@ # The original idea comes from Eric W. Biederman, in # http://article.gmane.org/gmane.comp.version-control.git/22407 -OPTIONS_KEEPDASHDASH= -OPTIONS_SPEC="\ -git-rebase [-i] [options] [--] <upstream> [<branch>] -git-rebase [-i] (--continue | --abort | --skip) --- - Available options are -v,verbose display a diffstat of what changed upstream -onto= rebase onto given branch instead of upstream -p,preserve-merges try to recreate merges instead of ignoring them -s,strategy= use the given merge strategy -no-ff cherry-pick all commits, even if unchanged -m,merge always used (no-op) -i,interactive always used (no-op) - Actions: -continue continue rebasing process -abort abort rebasing process and restore original branch -skip skip current patch and continue rebasing process -no-verify override pre-rebase hook from stopping the operation -verify allow pre-rebase hook to run -root rebase all reachable commmits up to the root(s) -autosquash move commits that begin with squash!/fixup! under -i -" - . git-sh-setup -require_work_tree - -DOTEST="$GIT_DIR/rebase-merge" # The file containing rebase commands, comments, and empty lines. # This file is created by "git rebase -i" then edited by the user. As # the lines are processed, they are removed from the front of this -# file and written to the tail of $DONE. -TODO="$DOTEST"/git-rebase-todo +# file and written to the tail of $done. +todo="$state_dir"/git-rebase-todo # The rebase command lines that have already been processed. A line # is moved here when it is first handled, before any associated user # actions. -DONE="$DOTEST"/done +done="$state_dir"/done # The commit message that is planned to be used for any changes that # need to be committed following a user interaction. -MSG="$DOTEST"/message +msg="$state_dir"/message # The file into which is accumulated the suggested commit message for # squash/fixup commands. When the first of a series of squash/fixups @@ -61,34 +35,34 @@ MSG="$DOTEST"/message # is appended to the file as it is processed. # # The first line of the file is of the form -# # This is a combination of $COUNT commits. -# where $COUNT is the number of commits whose messages have been +# # This is a combination of $count commits. +# where $count is the number of commits whose messages have been # written to the file so far (including the initial "pick" commit). # Each time that a commit message is processed, this line is read and # updated. It is deleted just before the combined commit is made. -SQUASH_MSG="$DOTEST"/message-squash +squash_msg="$state_dir"/message-squash # If the current series of squash/fixups has not yet included a squash # command, then this file exists and holds the commit message of the # original "pick" commit. (If the series ends without a "squash" # command, then this can be used as the commit message of the combined # commit without opening the editor.) -FIXUP_MSG="$DOTEST"/message-fixup +fixup_msg="$state_dir"/message-fixup -# $REWRITTEN is the name of a directory containing files for each -# commit that is reachable by at least one merge base of $HEAD and -# $UPSTREAM. They are not necessarily rewritten, but their children +# $rewritten is the name of a directory containing files for each +# commit that is reachable by at least one merge base of $head and +# $upstream. They are not necessarily rewritten, but their children # might be. This ensures that commits on merged, but otherwise # unrelated side branches are left alone. (Think "X" in the man page's # example.) -REWRITTEN="$DOTEST"/rewritten +rewritten="$state_dir"/rewritten -DROPPED="$DOTEST"/dropped +dropped="$state_dir"/dropped # A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and # GIT_AUTHOR_DATE that will be used for the commit that is currently # being rebased. -AUTHOR_SCRIPT="$DOTEST"/author-script +author_script="$state_dir"/author-script # When an "edit" rebase command is being processed, the SHA1 of the # commit to be edited is recorded in this file. When "git rebase @@ -96,69 +70,31 @@ AUTHOR_SCRIPT="$DOTEST"/author-script # will be amended to the HEAD commit, but only provided the HEAD # commit is still the commit to be edited. When any other rebase # command is processed, this file is deleted. -AMEND="$DOTEST"/amend +amend="$state_dir"/amend # For the post-rewrite hook, we make a list of rewritten commits and # their new sha1s. The rewritten-pending list keeps the sha1s of # commits that have been processed, but not committed yet, # e.g. because they are waiting for a 'squash' command. -REWRITTEN_LIST="$DOTEST"/rewritten-list -REWRITTEN_PENDING="$DOTEST"/rewritten-pending - -PRESERVE_MERGES= -STRATEGY= -ONTO= -VERBOSE= -OK_TO_SKIP_PRE_REBASE= -REBASE_ROOT= -AUTOSQUASH= -test "$(git config --bool rebase.autosquash)" = "true" && AUTOSQUASH=t -NEVER_FF= - -GIT_CHERRY_PICK_HELP="\ -hint: after resolving the conflicts, mark the corrected paths -hint: with 'git add <paths>' and run 'git rebase --continue'" +rewritten_list="$state_dir"/rewritten-list +rewritten_pending="$state_dir"/rewritten-pending + +GIT_CHERRY_PICK_HELP="$resolvemsg" export GIT_CHERRY_PICK_HELP warn () { printf '%s\n' "$*" >&2 } -output () { - case "$VERBOSE" in - '') - output=$("$@" 2>&1 ) - status=$? - test $status != 0 && printf "%s\n" "$output" - return $status - ;; - *) - "$@" - ;; - esac -} - # Output the commit message for the specified commit. commit_message () { git cat-file commit "$1" | sed "1,/^$/d" } -run_pre_rebase_hook () { - if test -z "$OK_TO_SKIP_PRE_REBASE" && - test -x "$GIT_DIR/hooks/pre-rebase" - then - "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || { - echo >&2 "The pre-rebase hook refused to rebase." - exit 1 - } - fi -} - - -ORIG_REFLOG_ACTION="$GIT_REFLOG_ACTION" +orig_reflog_action="$GIT_REFLOG_ACTION" comment_for_reflog () { - case "$ORIG_REFLOG_ACTION" in + case "$orig_reflog_action" in ''|rebase*) GIT_REFLOG_ACTION="rebase -i ($1)" export GIT_REFLOG_ACTION @@ -168,16 +104,16 @@ comment_for_reflog () { last_count= mark_action_done () { - sed -e 1q < "$TODO" >> "$DONE" - sed -e 1d < "$TODO" >> "$TODO".new - mv -f "$TODO".new "$TODO" - count=$(sane_grep -c '^[^#]' < "$DONE") - total=$(($count+$(sane_grep -c '^[^#]' < "$TODO"))) - if test "$last_count" != "$count" + sed -e 1q < "$todo" >> "$done" + sed -e 1d < "$todo" >> "$todo".new + mv -f "$todo".new "$todo" + new_count=$(sane_grep -c '^[^#]' < "$done") + total=$(($new_count+$(sane_grep -c '^[^#]' < "$todo"))) + if test "$last_count" != "$new_count" then - last_count=$count - printf "Rebasing (%d/%d)\r" $count $total - test -z "$VERBOSE" || echo + last_count=$new_count + printf "Rebasing (%d/%d)\r" $new_count $total + test -z "$verbose" || echo fi } @@ -193,22 +129,22 @@ make_patch () { *) echo "Root commit" ;; - esac > "$DOTEST"/patch - test -f "$MSG" || - commit_message "$1" > "$MSG" - test -f "$AUTHOR_SCRIPT" || - get_author_ident_from_commit "$1" > "$AUTHOR_SCRIPT" + esac > "$state_dir"/patch + test -f "$msg" || + commit_message "$1" > "$msg" + test -f "$author_script" || + get_author_ident_from_commit "$1" > "$author_script" } die_with_patch () { - echo "$1" > "$DOTEST"/stopped-sha + echo "$1" > "$state_dir"/stopped-sha make_patch "$1" git rerere die "$2" } die_abort () { - rm -rf "$DOTEST" + rm -rf "$state_dir" die "$1" } @@ -228,15 +164,10 @@ do_with_author () { pick_one () { ff=--ff case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac - case "$NEVER_FF" in '') ;; ?*) ff= ;; esac + case "$force_rebase" in '') ;; ?*) ff= ;; esac output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1" - test -d "$REWRITTEN" && + test -d "$rewritten" && pick_one_preserving_merges "$@" && return - if test -n "$REBASE_ROOT" - then - output git cherry-pick "$@" - return - fi output git cherry-pick $ff "$@" } @@ -253,20 +184,20 @@ pick_one_preserving_merges () { esac sha1=$(git rev-parse $sha1) - if test -f "$DOTEST"/current-commit + if test -f "$state_dir"/current-commit then if test "$fast_forward" = t then while read current_commit do - git rev-parse HEAD > "$REWRITTEN"/$current_commit - done <"$DOTEST"/current-commit - rm "$DOTEST"/current-commit || + git rev-parse HEAD > "$rewritten"/$current_commit + done <"$state_dir"/current-commit + rm "$state_dir"/current-commit || die "Cannot write current commit's replacement sha1" fi fi - echo $sha1 >> "$DOTEST"/current-commit + echo $sha1 >> "$state_dir"/current-commit # rewrite parents; if none were rewritten, we can fast-forward. new_parents= @@ -280,9 +211,9 @@ pick_one_preserving_merges () { p=$(expr "$pend" : ' \([^ ]*\)') pend="${pend# $p}" - if test -f "$REWRITTEN"/$p + if test -f "$rewritten"/$p then - new_p=$(cat "$REWRITTEN"/$p) + new_p=$(cat "$rewritten"/$p) # If the todo reordered commits, and our parent is marked for # rewriting, but hasn't been gotten to yet, assume the user meant to @@ -301,10 +232,10 @@ pick_one_preserving_merges () { ;; esac else - if test -f "$DROPPED"/$p + if test -f "$dropped"/$p then fast_forward=f - replacement="$(cat "$DROPPED"/$p)" + replacement="$(cat "$dropped"/$p)" test -z "$replacement" && replacement=root pend=" $replacement$pend" else @@ -333,18 +264,19 @@ pick_one_preserving_merges () { test "a$1" = a-n && die "Refusing to squash a merge: $sha1" # redo merge - author_script=$(get_author_ident_from_commit $sha1) - eval "$author_script" - msg="$(commit_message $sha1)" + author_script_content=$(get_author_ident_from_commit $sha1) + eval "$author_script_content" + msg_content="$(commit_message $sha1)" # No point in merging the first parent, that's HEAD new_parents=${new_parents# $first_parent} if ! do_with_author output \ - git merge $STRATEGY -m "$msg" $new_parents + git merge --no-ff ${strategy:+-s $strategy} -m \ + "$msg_content" $new_parents then - printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG + printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG die_with_patch $sha1 "Error redoing merge $sha1" fi - echo "$sha1 $(git rev-parse HEAD^0)" >> "$REWRITTEN_LIST" + echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list" ;; *) output git cherry-pick "$@" || @@ -365,46 +297,46 @@ nth_string () { } update_squash_messages () { - if test -f "$SQUASH_MSG"; then - mv "$SQUASH_MSG" "$SQUASH_MSG".bak || exit - COUNT=$(($(sed -n \ + if test -f "$squash_msg"; then + mv "$squash_msg" "$squash_msg".bak || exit + count=$(($(sed -n \ -e "1s/^# This is a combination of \(.*\) commits\./\1/p" \ - -e "q" < "$SQUASH_MSG".bak)+1)) + -e "q" < "$squash_msg".bak)+1)) { - echo "# This is a combination of $COUNT commits." + echo "# This is a combination of $count commits." sed -e 1d -e '2,/^./{ /^$/d - }' <"$SQUASH_MSG".bak - } >"$SQUASH_MSG" + }' <"$squash_msg".bak + } >"$squash_msg" else - commit_message HEAD > "$FIXUP_MSG" || die "Cannot write $FIXUP_MSG" - COUNT=2 + commit_message HEAD > "$fixup_msg" || die "Cannot write $fixup_msg" + count=2 { echo "# This is a combination of 2 commits." echo "# The first commit's message is:" echo - cat "$FIXUP_MSG" - } >"$SQUASH_MSG" + cat "$fixup_msg" + } >"$squash_msg" fi case $1 in squash) - rm -f "$FIXUP_MSG" + rm -f "$fixup_msg" echo - echo "# This is the $(nth_string $COUNT) commit message:" + echo "# This is the $(nth_string $count) commit message:" echo commit_message $2 ;; fixup) echo - echo "# The $(nth_string $COUNT) commit message will be skipped:" + echo "# The $(nth_string $count) commit message will be skipped:" echo commit_message $2 | sed -e 's/^/# /' ;; - esac >>"$SQUASH_MSG" + esac >>"$squash_msg" } peek_next_command () { - sed -n -e "/^#/d" -e '/^$/d' -e "s/ .*//p" -e "q" < "$TODO" + sed -n -e "/^#/d" -e '/^$/d' -e "s/ .*//p" -e "q" < "$todo" } # A squash/fixup has failed. Prepare the long version of the squash @@ -414,24 +346,24 @@ peek_next_command () { # messages, effectively causing the combined commit to be used as the # new basis for any further squash/fixups. Args: sha1 rest die_failed_squash() { - mv "$SQUASH_MSG" "$MSG" || exit - rm -f "$FIXUP_MSG" - cp "$MSG" "$GIT_DIR"/MERGE_MSG || exit + mv "$squash_msg" "$msg" || exit + rm -f "$fixup_msg" + cp "$msg" "$GIT_DIR"/MERGE_MSG || exit warn warn "Could not apply $1... $2" die_with_patch $1 "" } flush_rewritten_pending() { - test -s "$REWRITTEN_PENDING" || return + test -s "$rewritten_pending" || return newsha1="$(git rev-parse HEAD^0)" - sed "s/$/ $newsha1/" < "$REWRITTEN_PENDING" >> "$REWRITTEN_LIST" - rm -f "$REWRITTEN_PENDING" + sed "s/$/ $newsha1/" < "$rewritten_pending" >> "$rewritten_list" + rm -f "$rewritten_pending" } record_in_rewritten() { oldsha1="$(git rev-parse $1)" - echo "$oldsha1" >> "$REWRITTEN_PENDING" + echo "$oldsha1" >> "$rewritten_pending" case "$(peek_next_command)" in squash|s|fixup|f) @@ -443,8 +375,8 @@ record_in_rewritten() { } do_next () { - rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit - read -r command sha1 rest < "$TODO" + rm -f "$msg" "$author_script" "$amend" || exit + read -r command sha1 rest < "$todo" case "$command" in '#'*|''|noop) mark_action_done @@ -472,9 +404,9 @@ do_next () { mark_action_done pick_one $sha1 || die_with_patch $sha1 "Could not apply $sha1... $rest" - echo "$sha1" > "$DOTEST"/stopped-sha + echo "$sha1" > "$state_dir"/stopped-sha make_patch $sha1 - git rev-parse --verify HEAD > "$AMEND" + git rev-parse --verify HEAD > "$amend" warn "Stopped at $sha1... $rest" warn "You can amend the commit now, with" warn @@ -497,47 +429,47 @@ do_next () { esac comment_for_reflog $squash_style - test -f "$DONE" && has_action "$DONE" || + test -f "$done" && has_action "$done" || die "Cannot '$squash_style' without a previous commit" mark_action_done update_squash_messages $squash_style $sha1 - author_script=$(get_author_ident_from_commit HEAD) - echo "$author_script" > "$AUTHOR_SCRIPT" - eval "$author_script" + author_script_content=$(get_author_ident_from_commit HEAD) + echo "$author_script_content" > "$author_script" + eval "$author_script_content" output git reset --soft HEAD^ pick_one -n $sha1 || die_failed_squash $sha1 "$rest" case "$(peek_next_command)" in squash|s|fixup|f) # This is an intermediate commit; its message will only be # used in case of trouble. So use the long version: - do_with_author output git commit --no-verify -F "$SQUASH_MSG" || + do_with_author output git commit --no-verify -F "$squash_msg" || die_failed_squash $sha1 "$rest" ;; *) # This is the final command of this squash/fixup group - if test -f "$FIXUP_MSG" + if test -f "$fixup_msg" then - do_with_author git commit --no-verify -F "$FIXUP_MSG" || + do_with_author git commit --no-verify -F "$fixup_msg" || die_failed_squash $sha1 "$rest" else - cp "$SQUASH_MSG" "$GIT_DIR"/SQUASH_MSG || exit + cp "$squash_msg" "$GIT_DIR"/SQUASH_MSG || exit rm -f "$GIT_DIR"/MERGE_MSG do_with_author git commit --no-verify -e || die_failed_squash $sha1 "$rest" fi - rm -f "$SQUASH_MSG" "$FIXUP_MSG" + rm -f "$squash_msg" "$fixup_msg" ;; esac record_in_rewritten $sha1 ;; x|"exec") - read -r command rest < "$TODO" + read -r command rest < "$todo" mark_action_done printf 'Executing: %s\n' "$rest" # "exec" command doesn't take a sha1 in the todo-list. # => can't just use $sha1 here. - git rev-parse --verify HEAD > "$DOTEST"/stopped-sha + git rev-parse --verify HEAD > "$state_dir"/stopped-sha ${SHELL:-@SHELL_PATH@} -c "$rest" # Actual execution status=$? if test "$status" -ne 0 @@ -563,42 +495,40 @@ do_next () { warn "Unknown command: $command $sha1 $rest" if git rev-parse --verify -q "$sha1" >/dev/null then - die_with_patch $sha1 "Please fix this in the file $TODO." + die_with_patch $sha1 "Please fix this in the file $todo." else - die "Please fix this in the file $TODO." + die "Please fix this in the file $todo." fi ;; esac - test -s "$TODO" && return + test -s "$todo" && return comment_for_reflog finish && - HEADNAME=$(cat "$DOTEST"/head-name) && - OLDHEAD=$(cat "$DOTEST"/head) && - SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) && - NEWHEAD=$(git rev-parse HEAD) && - case $HEADNAME in + shortonto=$(git rev-parse --short $onto) && + newhead=$(git rev-parse HEAD) && + case $head_name in refs/*) - message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO" && - git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD && - git symbolic-ref HEAD $HEADNAME + message="$GIT_REFLOG_ACTION: $head_name onto $shortonto" && + git update-ref -m "$message" $head_name $newhead $orig_head && + git symbolic-ref HEAD $head_name ;; esac && { - test ! -f "$DOTEST"/verbose || - git diff-tree --stat $(cat "$DOTEST"/head)..HEAD + test ! -f "$state_dir"/verbose || + git diff-tree --stat $orig_head..HEAD } && { - test -s "$REWRITTEN_LIST" && - git notes copy --for-rewrite=rebase < "$REWRITTEN_LIST" || + test -s "$rewritten_list" && + git notes copy --for-rewrite=rebase < "$rewritten_list" || true # we don't care if this copying failed } && if test -x "$GIT_DIR"/hooks/post-rewrite && - test -s "$REWRITTEN_LIST"; then - "$GIT_DIR"/hooks/post-rewrite rebase < "$REWRITTEN_LIST" + test -s "$rewritten_list"; then + "$GIT_DIR"/hooks/post-rewrite rebase < "$rewritten_list" true # we don't care if this hook failed fi && - rm -rf "$DOTEST" && + rm -rf "$state_dir" && git gc --auto && - warn "Successfully rebased and updated $HEADNAME." + warn "Successfully rebased and updated $head_name." exit } @@ -618,11 +548,11 @@ skip_unnecessary_picks () { # fd=3 means we skip the command case "$fd,$command" in 3,pick|3,p) - # pick a commit whose parent is current $ONTO -> skip + # pick a commit whose parent is current $onto -> skip sha1=${rest%% *} case "$(git rev-parse --verify --quiet "$sha1"^)" in - "$ONTO"*) - ONTO=$sha1 + "$onto"*) + onto=$sha1 ;; *) fd=1 @@ -637,32 +567,16 @@ skip_unnecessary_picks () { ;; esac printf '%s\n' "$command${rest:+ }$rest" >&$fd - done <"$TODO" >"$TODO.new" 3>>"$DONE" && - mv -f "$TODO".new "$TODO" && + done <"$todo" >"$todo.new" 3>>"$done" && + mv -f "$todo".new "$todo" && case "$(peek_next_command)" in squash|s|fixup|f) - record_in_rewritten "$ONTO" + record_in_rewritten "$onto" ;; esac || die "Could not skip unnecessary pick commands" } -# check if no other options are set -is_standalone () { - test $# -eq 2 -a "$2" = '--' && - test -z "$ONTO" && - test -z "$PRESERVE_MERGES" && - test -z "$STRATEGY" && - test -z "$VERBOSE" -} - -get_saved_options () { - test -d "$REWRITTEN" && PRESERVE_MERGES=t - test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)" - test -f "$DOTEST"/verbose && VERBOSE=t - test -f "$DOTEST"/rebase-root && REBASE_ROOT=t -} - # Rearrange the todo list that has both "pick sha1 msg" and # "pick sha1 fixup!/squash! msg" appears in it so that the latter # comes immediately after the former, and change "pick" to @@ -699,7 +613,7 @@ rearrange_squash () { esac printf '%s\n' "$pick $sha1 $message" used="$used$sha1 " - while read -r squash action msg + while read -r squash action msg_content do case " $used" in *" $squash "*) continue ;; @@ -709,13 +623,13 @@ rearrange_squash () { +*) action="${action#+}" # full sha1 prefix test - case "$msg" in "$sha1"*) emit=1;; esac ;; + case "$msg_content" in "$sha1"*) emit=1;; esac ;; *) # message prefix test - case "$message" in "$msg"*) emit=1;; esac ;; + case "$message" in "$msg_content"*) emit=1;; esac ;; esac if test $emit = 1; then - printf '%s\n' "$action $squash $action! $msg" + printf '%s\n' "$action $squash $action! $msg_content" used="$used$squash " fi done <"$1.sq" @@ -724,296 +638,159 @@ rearrange_squash () { rm -f "$1.sq" "$1.rearranged" } -LF=' -' -parse_onto () { - case "$1" in - *...*) - if left=${1%...*} right=${1#*...} && - onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD}) - then - case "$onto" in - ?*"$LF"?* | '') - exit 1 ;; - esac - echo "$onto" - exit 0 - fi - esac - git rev-parse --verify "$1^0" -} - -while test $# != 0 -do - case "$1" in - --no-verify) - OK_TO_SKIP_PRE_REBASE=yes - ;; - --verify) - OK_TO_SKIP_PRE_REBASE= - ;; - --continue) - is_standalone "$@" || usage - get_saved_options - comment_for_reflog continue - - test -d "$DOTEST" || die "No interactive rebase running" - - # Sanity check - git rev-parse --verify HEAD >/dev/null || - die "Cannot read HEAD" - git update-index --ignore-submodules --refresh && - git diff-files --quiet --ignore-submodules || - die "Working tree is dirty" - - # do we have anything to commit? - if git diff-index --cached --quiet --ignore-submodules HEAD -- +case "$action" in +continue) + # do we have anything to commit? + if git diff-index --cached --quiet --ignore-submodules HEAD -- + then + : Nothing to commit -- skip this + else + . "$author_script" || + die "Cannot find the author identity" + current_head= + if test -f "$amend" then - : Nothing to commit -- skip this - else - . "$AUTHOR_SCRIPT" || - die "Cannot find the author identity" - amend= - if test -f "$AMEND" - then - amend=$(git rev-parse --verify HEAD) - test "$amend" = $(cat "$AMEND") || - die "\ + current_head=$(git rev-parse --verify HEAD) + test "$current_head" = $(cat "$amend") || + die "\ You have uncommitted changes in your working tree. Please, commit them first and then run 'git rebase --continue' again." - git reset --soft HEAD^ || - die "Cannot rewind the HEAD" - fi - do_with_author git commit --no-verify -F "$MSG" -e || { - test -n "$amend" && git reset --soft $amend - die "Could not commit staged changes." - } + git reset --soft HEAD^ || + die "Cannot rewind the HEAD" fi + do_with_author git commit --no-verify -F "$msg" -e || { + test -n "$current_head" && git reset --soft $current_head + die "Could not commit staged changes." + } + fi - record_in_rewritten "$(cat "$DOTEST"/stopped-sha)" - - require_clean_work_tree "rebase" - do_rest - ;; - --abort) - is_standalone "$@" || usage - get_saved_options - comment_for_reflog abort - - git rerere clear - test -d "$DOTEST" || die "No interactive rebase running" - - HEADNAME=$(cat "$DOTEST"/head-name) - HEAD=$(cat "$DOTEST"/head) - case $HEADNAME in - refs/*) - git symbolic-ref HEAD $HEADNAME - ;; - esac && - output git reset --hard $HEAD && - rm -rf "$DOTEST" - exit - ;; - --skip) - is_standalone "$@" || usage - get_saved_options - comment_for_reflog skip - - git rerere clear - test -d "$DOTEST" || die "No interactive rebase running" - - output git reset --hard && do_rest - ;; - -s) - case "$#,$1" in - *,*=*) - STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;; - 1,*) - usage ;; - *) - STRATEGY="-s $2" - shift ;; - esac - ;; - -m) - # we use merge anyway - ;; - -v) - VERBOSE=t - ;; - -p) - PRESERVE_MERGES=t - ;; - -i) - # yeah, we know - ;; - --no-ff) - NEVER_FF=t - ;; - --root) - REBASE_ROOT=t - ;; - --autosquash) - AUTOSQUASH=t - ;; - --no-autosquash) - AUTOSQUASH= - ;; - --onto) - shift - ONTO=$(parse_onto "$1") || - die "Does not point to a valid commit: $1" - ;; - --) - shift - test -z "$REBASE_ROOT" -a $# -ge 1 -a $# -le 2 || - test ! -z "$REBASE_ROOT" -a $# -le 1 || usage - test -d "$DOTEST" && - die "Interactive rebase already started" - - git var GIT_COMMITTER_IDENT >/dev/null || - die "You need to set your committer info first" + record_in_rewritten "$(cat "$state_dir"/stopped-sha)" - if test -z "$REBASE_ROOT" - then - UPSTREAM_ARG="$1" - UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base" - test -z "$ONTO" && ONTO=$UPSTREAM - shift - else - UPSTREAM= - UPSTREAM_ARG=--root - test -z "$ONTO" && - die "You must specify --onto when using --root" - fi - run_pre_rebase_hook "$UPSTREAM_ARG" "$@" + require_clean_work_tree "rebase" + do_rest + ;; +skip) + git rerere clear - comment_for_reflog start + do_rest + ;; +esac - require_clean_work_tree "rebase" "Please commit or stash them." +git var GIT_COMMITTER_IDENT >/dev/null || + die "You need to set your committer info first" - if test ! -z "$1" - then - output git checkout "$1" -- || - die "Could not checkout $1" - fi +comment_for_reflog start - HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?" - mkdir "$DOTEST" || die "Could not create temporary $DOTEST" +if test ! -z "$switch_to" +then + output git checkout "$switch_to" -- || + die "Could not checkout $switch_to" +fi - : > "$DOTEST"/interactive || die "Could not mark as interactive" - git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null || - echo "detached HEAD" > "$DOTEST"/head-name +orig_head=$(git rev-parse --verify HEAD) || die "No HEAD?" +mkdir "$state_dir" || die "Could not create temporary $state_dir" - echo $HEAD > "$DOTEST"/head - case "$REBASE_ROOT" in - '') - rm -f "$DOTEST"/rebase-root ;; - *) - : >"$DOTEST"/rebase-root ;; - esac - echo $ONTO > "$DOTEST"/onto - test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy - test t = "$VERBOSE" && : > "$DOTEST"/verbose - if test t = "$PRESERVE_MERGES" - then - if test -z "$REBASE_ROOT" - then - mkdir "$REWRITTEN" && - for c in $(git merge-base --all $HEAD $UPSTREAM) - do - echo $ONTO > "$REWRITTEN"/$c || - die "Could not init rewritten commits" - done - else - mkdir "$REWRITTEN" && - echo $ONTO > "$REWRITTEN"/root || - die "Could not init rewritten commits" - fi - # No cherry-pick because our first pass is to determine - # parents to rewrite and skipping dropped commits would - # prematurely end our probe - MERGES_OPTION= - first_after_upstream="$(git rev-list --reverse --first-parent $UPSTREAM..$HEAD | head -n 1)" - else - MERGES_OPTION="--no-merges --cherry-pick" - fi - - SHORTHEAD=$(git rev-parse --short $HEAD) - SHORTONTO=$(git rev-parse --short $ONTO) - if test -z "$REBASE_ROOT" - # this is now equivalent to ! -z "$UPSTREAM" - then - SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM) - REVISIONS=$UPSTREAM...$HEAD - SHORTREVISIONS=$SHORTUPSTREAM..$SHORTHEAD - else - REVISIONS=$ONTO...$HEAD - SHORTREVISIONS=$SHORTHEAD - fi - git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \ - --abbrev=7 --reverse --left-right --topo-order \ - $REVISIONS | \ - sed -n "s/^>//p" | - while read -r shortsha1 rest +: > "$state_dir"/interactive || die "Could not mark as interactive" +write_basic_state +if test t = "$preserve_merges" +then + if test -z "$rebase_root" + then + mkdir "$rewritten" && + for c in $(git merge-base --all $orig_head $upstream) do - if test t != "$PRESERVE_MERGES" - then - printf '%s\n' "pick $shortsha1 $rest" >> "$TODO" - else - sha1=$(git rev-parse $shortsha1) - if test -z "$REBASE_ROOT" - then - preserve=t - for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-) - do - if test -f "$REWRITTEN"/$p -a \( $p != $ONTO -o $sha1 = $first_after_upstream \) - then - preserve=f - fi - done - else - preserve=f - fi - if test f = "$preserve" - then - touch "$REWRITTEN"/$sha1 - printf '%s\n' "pick $shortsha1 $rest" >> "$TODO" - fi - fi + echo $onto > "$rewritten"/$c || + die "Could not init rewritten commits" done - - # Watch for commits that been dropped by --cherry-pick - if test t = "$PRESERVE_MERGES" + else + mkdir "$rewritten" && + echo $onto > "$rewritten"/root || + die "Could not init rewritten commits" + fi + # No cherry-pick because our first pass is to determine + # parents to rewrite and skipping dropped commits would + # prematurely end our probe + merges_option= + first_after_upstream="$(git rev-list --reverse --first-parent $upstream..$orig_head | head -n 1)" +else + merges_option="--no-merges --cherry-pick" +fi + +shorthead=$(git rev-parse --short $orig_head) +shortonto=$(git rev-parse --short $onto) +if test -z "$rebase_root" + # this is now equivalent to ! -z "$upstream" +then + shortupstream=$(git rev-parse --short $upstream) + revisions=$upstream...$orig_head + shortrevisions=$shortupstream..$shorthead +else + revisions=$onto...$orig_head + shortrevisions=$shorthead +fi +git rev-list $merges_option --pretty=oneline --abbrev-commit \ + --abbrev=7 --reverse --left-right --topo-order \ + $revisions | \ + sed -n "s/^>//p" | +while read -r shortsha1 rest +do + if test t != "$preserve_merges" + then + printf '%s\n' "pick $shortsha1 $rest" >> "$todo" + else + sha1=$(git rev-parse $shortsha1) + if test -z "$rebase_root" then - mkdir "$DROPPED" - # Save all non-cherry-picked changes - git rev-list $REVISIONS --left-right --cherry-pick | \ - sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks - # Now all commits and note which ones are missing in - # not-cherry-picks and hence being dropped - git rev-list $REVISIONS | - while read rev + preserve=t + for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-) do - if test -f "$REWRITTEN"/$rev -a "$(sane_grep "$rev" "$DOTEST"/not-cherry-picks)" = "" + if test -f "$rewritten"/$p -a \( $p != $onto -o $sha1 = $first_after_upstream \) then - # Use -f2 because if rev-list is telling us this commit is - # not worthwhile, we don't want to track its multiple heads, - # just the history of its first-parent for others that will - # be rebasing on top of it - git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$DROPPED"/$rev - short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev) - sane_grep -v "^[a-z][a-z]* $short" <"$TODO" > "${TODO}2" ; mv "${TODO}2" "$TODO" - rm "$REWRITTEN"/$rev + preserve=f fi done + else + preserve=f + fi + if test f = "$preserve" + then + touch "$rewritten"/$sha1 + printf '%s\n' "pick $shortsha1 $rest" >> "$todo" fi + fi +done - test -s "$TODO" || echo noop >> "$TODO" - test -n "$AUTOSQUASH" && rearrange_squash "$TODO" - cat >> "$TODO" << EOF +# Watch for commits that been dropped by --cherry-pick +if test t = "$preserve_merges" +then + mkdir "$dropped" + # Save all non-cherry-picked changes + git rev-list $revisions --left-right --cherry-pick | \ + sed -n "s/^>//p" > "$state_dir"/not-cherry-picks + # Now all commits and note which ones are missing in + # not-cherry-picks and hence being dropped + git rev-list $revisions | + while read rev + do + if test -f "$rewritten"/$rev -a "$(sane_grep "$rev" "$state_dir"/not-cherry-picks)" = "" + then + # Use -f2 because if rev-list is telling us this commit is + # not worthwhile, we don't want to track its multiple heads, + # just the history of its first-parent for others that will + # be rebasing on top of it + git rev-list --parents -1 $rev | cut -d' ' -s -f2 > "$dropped"/$rev + short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev) + sane_grep -v "^[a-z][a-z]* $short" <"$todo" > "${todo}2" ; mv "${todo}2" "$todo" + rm "$rewritten"/$rev + fi + done +fi + +test -s "$todo" || echo noop >> "$todo" +test -n "$autosquash" && rearrange_squash "$todo" +cat >> "$todo" << EOF -# Rebase $SHORTREVISIONS onto $SHORTONTO +# Rebase $shortrevisions onto $shortonto # # Commands: # p, pick = use commit @@ -1028,22 +805,18 @@ first and then run 'git rebase --continue' again." # EOF - has_action "$TODO" || - die_abort "Nothing to do" +has_action "$todo" || + die_abort "Nothing to do" - cp "$TODO" "$TODO".backup - git_editor "$TODO" || - die_abort "Could not execute editor" +cp "$todo" "$todo".backup +git_editor "$todo" || + die_abort "Could not execute editor" - has_action "$TODO" || - die_abort "Nothing to do" +has_action "$todo" || + die_abort "Nothing to do" - test -d "$REWRITTEN" || test -n "$NEVER_FF" || skip_unnecessary_picks +test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks - output git checkout $ONTO || die_abort "could not detach HEAD" - git update-ref ORIG_HEAD $HEAD - do_rest - ;; - esac - shift -done +output git checkout $onto || die_abort "could not detach HEAD" +git update-ref ORIG_HEAD $orig_head +do_rest diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh new file mode 100644 index 0000000000..26afc75cc7 --- /dev/null +++ b/git-rebase--merge.sh @@ -0,0 +1,151 @@ +#!/bin/sh +# +# Copyright (c) 2010 Junio C Hamano. +# + +. git-sh-setup + +prec=4 + +read_state () { + onto_name=$(cat "$state_dir"/onto_name) && + end=$(cat "$state_dir"/end) && + msgnum=$(cat "$state_dir"/msgnum) +} + +continue_merge () { + test -d "$state_dir" || die "$state_dir directory does not exist" + + unmerged=$(git ls-files -u) + if test -n "$unmerged" + then + echo "You still have unmerged paths in your index" + echo "did you forget to use git add?" + die "$resolvemsg" + fi + + cmt=`cat "$state_dir/current"` + if ! git diff-index --quiet --ignore-submodules HEAD -- + then + if ! git commit --no-verify -C "$cmt" + then + echo "Commit failed, please do not call \"git commit\"" + echo "directly, but instead do one of the following: " + die "$resolvemsg" + fi + if test -z "$GIT_QUIET" + then + printf "Committed: %0${prec}d " $msgnum + fi + echo "$cmt $(git rev-parse HEAD^0)" >> "$state_dir/rewritten" + else + if test -z "$GIT_QUIET" + then + printf "Already applied: %0${prec}d " $msgnum + fi + fi + test -z "$GIT_QUIET" && + GIT_PAGER='' git log --format=%s -1 "$cmt" + + # onto the next patch: + msgnum=$(($msgnum + 1)) + echo "$msgnum" >"$state_dir/msgnum" +} + +call_merge () { + cmt="$(cat "$state_dir/cmt.$1")" + echo "$cmt" > "$state_dir/current" + hd=$(git rev-parse --verify HEAD) + cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD) + msgnum=$(cat "$state_dir/msgnum") + eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"' + eval GITHEAD_$hd='$onto_name' + export GITHEAD_$cmt GITHEAD_$hd + if test -n "$GIT_QUIET" + then + GIT_MERGE_VERBOSITY=1 && export GIT_MERGE_VERBOSITY + fi + test -z "$strategy" && strategy=recursive + eval 'git-merge-$strategy' $strategy_opts '"$cmt^" -- "$hd" "$cmt"' + rv=$? + case "$rv" in + 0) + unset GITHEAD_$cmt GITHEAD_$hd + return + ;; + 1) + git rerere $allow_rerere_autoupdate + die "$resolvemsg" + ;; + 2) + echo "Strategy: $strategy failed, try another" 1>&2 + die "$resolvemsg" + ;; + *) + die "Unknown exit code ($rv) from command:" \ + "git-merge-$strategy $cmt^ -- HEAD $cmt" + ;; + esac +} + +finish_rb_merge () { + move_to_original_branch + git notes copy --for-rewrite=rebase < "$state_dir"/rewritten + if test -x "$GIT_DIR"/hooks/post-rewrite && + test -s "$state_dir"/rewritten; then + "$GIT_DIR"/hooks/post-rewrite rebase < "$state_dir"/rewritten + fi + rm -r "$state_dir" + say All done. +} + +case "$action" in +continue) + read_state + continue_merge + while test "$msgnum" -le "$end" + do + call_merge "$msgnum" + continue_merge + done + finish_rb_merge + exit + ;; +skip) + read_state + git rerere clear + msgnum=$(($msgnum + 1)) + while test "$msgnum" -le "$end" + do + call_merge "$msgnum" + continue_merge + done + finish_rb_merge + exit + ;; +esac + +mkdir -p "$state_dir" +echo "$onto_name" > "$state_dir/onto_name" +write_basic_state + +msgnum=0 +for cmt in `git rev-list --reverse --no-merges "$revisions"` +do + msgnum=$(($msgnum + 1)) + echo "$cmt" > "$state_dir/cmt.$msgnum" +done + +echo 1 >"$state_dir/msgnum" +echo $msgnum >"$state_dir/end" + +end=$msgnum +msgnum=1 + +while test "$msgnum" -le "$end" +do + call_merge "$msgnum" + continue_merge +done + +finish_rb_merge diff --git a/git-rebase.sh b/git-rebase.sh index cbb0ea90ed..7a54bfc618 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano. # -USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto <newbase>] (<upstream>|--root) [<branch>] [--quiet | -q]' +USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]' LONG_USAGE='git-rebase replaces <branch> with a new branch of the same name. When the --onto option is provided the new branch starts out with a HEAD equal to <newbase>, otherwise it is equal to <upstream> @@ -28,7 +28,39 @@ Example: git-rebase master~1 topic ' SUBDIRECTORY_OK=Yes -OPTIONS_SPEC= +OPTIONS_KEEPDASHDASH= +OPTIONS_SPEC="\ +git rebase [-i] [options] [--onto <newbase>] [<upstream>] [<branch>] +git rebase [-i] [options] --onto <newbase> --root [<branch>] +git-rebase [-i] --continue | --abort | --skip +-- + Available options are +v,verbose! display a diffstat of what changed upstream +q,quiet! be quiet. implies --no-stat +onto=! rebase onto given branch instead of upstream +p,preserve-merges! try to recreate merges instead of ignoring them +s,strategy=! use the given merge strategy +no-ff! cherry-pick all commits, even if unchanged +m,merge! use merging strategies to rebase +i,interactive! let the user edit the list of commits to rebase +f,force-rebase! force rebase even if branch is up to date +X,strategy-option=! pass the argument through to the merge strategy +stat! display a diffstat of what changed upstream +n,no-stat! do not show diffstat of what changed upstream +verify allow pre-rebase hook to run +rerere-autoupdate allow rerere to update index with resolved conflicts +root! rebase all reachable commits up to the root(s) +autosquash move commits that begin with squash!/fixup! under -i +committer-date-is-author-date! passed to 'git am' +ignore-date! passed to 'git am' +whitespace=! passed to 'git apply' +ignore-whitespace! passed to 'git apply' +C=! passed to 'git apply' + Actions: +continue! continue rebasing process +abort! abort rebasing process and restore original branch +skip! skip current patch and continue rebasing process +" . git-sh-setup set_reflog_action rebase require_work_tree @@ -36,18 +68,18 @@ cd_to_toplevel LF=' ' -OK_TO_SKIP_PRE_REBASE= -RESOLVEMSG=" +ok_to_skip_pre_rebase= +resolvemsg=" When you have resolved this problem run \"git rebase --continue\". If you would prefer to skip this patch, instead run \"git rebase --skip\". To restore the original branch and stop rebasing run \"git rebase --abort\". " -unset newbase -strategy=recursive +unset onto +strategy= strategy_opts= do_merge= -dotest="$GIT_DIR"/rebase-merge -prec=4 +merge_dir="$GIT_DIR"/rebase-merge +apply_dir="$GIT_DIR"/rebase-apply verbose= diffstat= test "$(git config --bool rebase.stat)" = true && diffstat=t @@ -55,92 +87,67 @@ git_am_opt= rebase_root= force_rebase= allow_rerere_autoupdate= - -continue_merge () { - test -n "$prev_head" || die "prev_head must be defined" - test -d "$dotest" || die "$dotest directory does not exist" - - unmerged=$(git ls-files -u) - if test -n "$unmerged" - then - echo "You still have unmerged paths in your index" - echo "did you forget to use git add?" - die "$RESOLVEMSG" - fi - - cmt=`cat "$dotest/current"` - if ! git diff-index --quiet --ignore-submodules HEAD -- +# Non-empty if a rebase was in progress when 'git rebase' was invoked +in_progress= +# One of {am, merge, interactive} +type= +# One of {"$GIT_DIR"/rebase-apply, "$GIT_DIR"/rebase-merge} +state_dir= +# One of {'', continue, skip, abort}, as parsed from command line +action= +preserve_merges= +autosquash= +test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t + +read_basic_state () { + head_name=$(cat "$state_dir"/head-name) && + onto=$(cat "$state_dir"/onto) && + # We always write to orig-head, but interactive rebase used to write to + # head. Fall back to reading from head to cover for the case that the + # user upgraded git with an ongoing interactive rebase. + if test -f "$state_dir"/orig-head then - if ! git commit --no-verify -C "$cmt" - then - echo "Commit failed, please do not call \"git commit\"" - echo "directly, but instead do one of the following: " - die "$RESOLVEMSG" - fi - if test -z "$GIT_QUIET" - then - printf "Committed: %0${prec}d " $msgnum - fi - echo "$cmt $(git rev-parse HEAD^0)" >> "$dotest/rewritten" + orig_head=$(cat "$state_dir"/orig-head) else - if test -z "$GIT_QUIET" - then - printf "Already applied: %0${prec}d " $msgnum - fi - fi - test -z "$GIT_QUIET" && - GIT_PAGER='' git log --format=%s -1 "$cmt" - - prev_head=`git rev-parse HEAD^0` - # save the resulting commit so we can read-tree on it later - echo "$prev_head" > "$dotest/prev_head" + orig_head=$(cat "$state_dir"/head) + fi && + GIT_QUIET=$(cat "$state_dir"/quiet) && + test -f "$state_dir"/verbose && verbose=t + test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)" + test -f "$state_dir"/strategy_opts && + strategy_opts="$(cat "$state_dir"/strategy_opts)" + test -f "$state_dir"/allow_rerere_autoupdate && + allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)" +} - # onto the next patch: - msgnum=$(($msgnum + 1)) - echo "$msgnum" >"$dotest/msgnum" +write_basic_state () { + echo "$head_name" > "$state_dir"/head-name && + echo "$onto" > "$state_dir"/onto && + echo "$orig_head" > "$state_dir"/orig-head && + echo "$GIT_QUIET" > "$state_dir"/quiet && + test t = "$verbose" && : > "$state_dir"/verbose + test -n "$strategy" && echo "$strategy" > "$state_dir"/strategy + test -n "$strategy_opts" && echo "$strategy_opts" > \ + "$state_dir"/strategy_opts + test -n "$allow_rerere_autoupdate" && echo "$allow_rerere_autoupdate" > \ + "$state_dir"/allow_rerere_autoupdate } -call_merge () { - cmt="$(cat "$dotest/cmt.$1")" - echo "$cmt" > "$dotest/current" - hd=$(git rev-parse --verify HEAD) - cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD) - msgnum=$(cat "$dotest/msgnum") - end=$(cat "$dotest/end") - eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"' - eval GITHEAD_$hd='$(cat "$dotest/onto_name")' - export GITHEAD_$cmt GITHEAD_$hd - if test -n "$GIT_QUIET" - then - GIT_MERGE_VERBOSITY=1 && export GIT_MERGE_VERBOSITY - fi - eval 'git-merge-$strategy' $strategy_opts '"$cmt^" -- "$hd" "$cmt"' - rv=$? - case "$rv" in - 0) - unset GITHEAD_$cmt GITHEAD_$hd - return - ;; - 1) - git rerere $allow_rerere_autoupdate - die "$RESOLVEMSG" - ;; - 2) - echo "Strategy: $rv $strategy failed, try another" 1>&2 - die "$RESOLVEMSG" +output () { + case "$verbose" in + '') + output=$("$@" 2>&1 ) + status=$? + test $status != 0 && printf "%s\n" "$output" + return $status ;; *) - die "Unknown exit code ($rv) from command:" \ - "git-merge-$strategy $cmt^ -- HEAD $cmt" + "$@" ;; esac } move_to_original_branch () { - test -z "$head_name" && - head_name="$(cat "$dotest"/head-name)" && - onto="$(cat "$dotest"/onto)" && - orig_head="$(cat "$dotest"/orig-head)" case "$head_name" in refs/*) message="rebase finished: $head_name onto $onto" @@ -152,42 +159,16 @@ move_to_original_branch () { esac } -finish_rb_merge () { - move_to_original_branch - git notes copy --for-rewrite=rebase < "$dotest"/rewritten - if test -x "$GIT_DIR"/hooks/post-rewrite && - test -s "$dotest"/rewritten; then - "$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten - fi - rm -r "$dotest" - say All done. -} - -is_interactive () { - while test $# != 0 - do - case "$1" in - -i|--interactive) - interactive_rebase=explicit - break - ;; - -p|--preserve-merges) - interactive_rebase=implied - ;; - esac - shift - done - +run_specific_rebase () { if [ "$interactive_rebase" = implied ]; then GIT_EDITOR=: export GIT_EDITOR fi - - test -n "$interactive_rebase" || test -f "$dotest"/interactive + . git-rebase--$type } run_pre_rebase_hook () { - if test -z "$OK_TO_SKIP_PRE_REBASE" && + if test -z "$ok_to_skip_pre_rebase" && test -x "$GIT_DIR/hooks/pre-rebase" then "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || @@ -195,163 +176,94 @@ run_pre_rebase_hook () { fi } -test -f "$GIT_DIR"/rebase-apply/applying && +test -f "$apply_dir"/applying && die 'It looks like git-am is in progress. Cannot rebase.' -is_interactive "$@" && exec git-rebase--interactive "$@" +if test -d "$apply_dir" +then + type=am + state_dir="$apply_dir" +elif test -d "$merge_dir" +then + if test -f "$merge_dir"/interactive + then + type=interactive + interactive_rebase=explicit + else + type=merge + fi + state_dir="$merge_dir" +fi +test -n "$type" && in_progress=t +total_argc=$# while test $# != 0 do case "$1" in --no-verify) - OK_TO_SKIP_PRE_REBASE=yes + ok_to_skip_pre_rebase=yes ;; --verify) - OK_TO_SKIP_PRE_REBASE= - ;; - --continue) - test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || - die "No rebase in progress?" - - git update-index --ignore-submodules --refresh && - git diff-files --quiet --ignore-submodules || { - echo "You must edit all merge conflicts and then" - echo "mark them as resolved using git add" - exit 1 - } - if test -d "$dotest" - then - prev_head=$(cat "$dotest/prev_head") - end=$(cat "$dotest/end") - msgnum=$(cat "$dotest/msgnum") - onto=$(cat "$dotest/onto") - GIT_QUIET=$(cat "$dotest/quiet") - continue_merge - while test "$msgnum" -le "$end" - do - call_merge "$msgnum" - continue_merge - done - finish_rb_merge - exit - fi - head_name=$(cat "$GIT_DIR"/rebase-apply/head-name) && - onto=$(cat "$GIT_DIR"/rebase-apply/onto) && - orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head) && - GIT_QUIET=$(cat "$GIT_DIR"/rebase-apply/quiet) - git am --resolved --3way --resolvemsg="$RESOLVEMSG" && - move_to_original_branch - exit + ok_to_skip_pre_rebase= ;; - --skip) - test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || - die "No rebase in progress?" - - git reset --hard HEAD || exit $? - if test -d "$dotest" - then - git rerere clear - prev_head=$(cat "$dotest/prev_head") - end=$(cat "$dotest/end") - msgnum=$(cat "$dotest/msgnum") - msgnum=$(($msgnum + 1)) - onto=$(cat "$dotest/onto") - GIT_QUIET=$(cat "$dotest/quiet") - while test "$msgnum" -le "$end" - do - call_merge "$msgnum" - continue_merge - done - finish_rb_merge - exit - fi - head_name=$(cat "$GIT_DIR"/rebase-apply/head-name) && - onto=$(cat "$GIT_DIR"/rebase-apply/onto) && - orig_head=$(cat "$GIT_DIR"/rebase-apply/orig-head) && - GIT_QUIET=$(cat "$GIT_DIR"/rebase-apply/quiet) - git am -3 --skip --resolvemsg="$RESOLVEMSG" && - move_to_original_branch - exit - ;; - --abort) - test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || - die "No rebase in progress?" - - git rerere clear - - test -d "$dotest" || dotest="$GIT_DIR"/rebase-apply - - head_name="$(cat "$dotest"/head-name)" && - case "$head_name" in - refs/*) - git symbolic-ref HEAD $head_name || - die "Could not move back to $head_name" - ;; - esac - git reset --hard $(cat "$dotest/orig-head") - rm -r "$dotest" - exit + --continue|--skip|--abort) + test $total_argc -eq 2 || usage + action=${1##--} ;; --onto) test 2 -le "$#" || usage - newbase="$2" + onto="$2" shift ;; - -M|-m|--m|--me|--mer|--merg|--merge) + -i) + interactive_rebase=explicit + ;; + -p) + preserve_merges=t + test -z "$interactive_rebase" && interactive_rebase=implied + ;; + --autosquash) + autosquash=t + ;; + --no-autosquash) + autosquash= + ;; + -M|-m) do_merge=t ;; - -X*|--strategy-option*) - case "$#,$1" in - 1,-X|1,--strategy-option) - usage ;; - *,-X|*,--strategy-option) - newopt="$2" - shift ;; - *,--strategy-option=*) - newopt="$(expr " $1" : ' --strategy-option=\(.*\)')" ;; - *,-X*) - newopt="$(expr " $1" : ' -X\(.*\)')" ;; - 1,*) - usage ;; - esac - strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$newopt")" + -X) + shift + strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$1")" do_merge=t + test -z "$strategy" && strategy=recursive ;; - -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\ - --strateg=*|--strategy=*|\ - -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) - case "$#,$1" in - *,*=*) - strategy=`expr "z$1" : 'z-[^=]*=\(.*\)'` ;; - 1,*) - usage ;; - *) - strategy="$2" - shift ;; - esac + -s) + shift + strategy="$1" do_merge=t ;; - -n|--no-stat) + -n) diffstat= ;; --stat) diffstat=t ;; - -v|--verbose) + -v) verbose=t diffstat=t GIT_QUIET= ;; - -q|--quiet) + -q) GIT_QUIET=t git_am_opt="$git_am_opt -q" verbose= diffstat= ;; - --whitespace=*) - git_am_opt="$git_am_opt $1" + --whitespace) + shift + git_am_opt="$git_am_opt --whitespace=$1" case "$1" in - --whitespace=fix|--whitespace=strip) + fix|strip) force_rebase=t ;; esac @@ -363,22 +275,21 @@ do git_am_opt="$git_am_opt $1" force_rebase=t ;; - -C*) - git_am_opt="$git_am_opt $1" + -C) + shift + git_am_opt="$git_am_opt -C$1" ;; --root) rebase_root=t ;; - -f|--f|--fo|--for|--forc|--force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase|--no-ff) + -f|--no-ff) force_rebase=t ;; --rerere-autoupdate|--no-rerere-autoupdate) allow_rerere_autoupdate="$1" ;; - -*) - usage - ;; - *) + --) + shift break ;; esac @@ -386,58 +297,106 @@ do done test $# -gt 2 && usage -if test $# -eq 0 && test -z "$rebase_root" +if test -n "$action" then - test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || usage - test -d "$dotest" -o -f "$GIT_DIR"/rebase-apply/rebasing && - die 'A rebase is in progress, try --continue, --skip or --abort.' + test -z "$in_progress" && die "No rebase in progress?" + # Only interactive rebase uses detailed reflog messages + if test "$type" = interactive && test "$GIT_REFLOG_ACTION" = rebase + then + GIT_REFLOG_ACTION="rebase -i ($action)" + export GIT_REFLOG_ACTION + fi fi -# Make sure we do not have $GIT_DIR/rebase-apply -if test -z "$do_merge" +case "$action" in +continue) + # Sanity check + git rev-parse --verify HEAD >/dev/null || + die "Cannot read HEAD" + git update-index --ignore-submodules --refresh && + git diff-files --quiet --ignore-submodules || { + echo "You must edit all merge conflicts and then" + echo "mark them as resolved using git add" + exit 1 + } + read_basic_state + run_specific_rebase + ;; +skip) + output git reset --hard HEAD || exit $? + read_basic_state + run_specific_rebase + ;; +abort) + git rerere clear + read_basic_state + case "$head_name" in + refs/*) + git symbolic-ref HEAD $head_name || + die "Could not move back to $head_name" + ;; + esac + output git reset --hard $orig_head + rm -r "$state_dir" + exit + ;; +esac + +# Make sure no rebase is in progress +if test -n "$in_progress" then - if mkdir "$GIT_DIR"/rebase-apply 2>/dev/null - then - rmdir "$GIT_DIR"/rebase-apply - else - echo >&2 ' -It seems that I cannot create a rebase-apply directory, and -I wonder if you are in the middle of patch application or another -rebase. If that is not the case, please - rm -fr '"$GIT_DIR"'/rebase-apply + die ' +It seems that there is already a '"${state_dir##*/}"' directory, and +I wonder if you are in the middle of another rebase. If that is the +case, please try + git rebase (--continue | --abort | --skip) +If that is not the case, please + rm -fr '"$state_dir"' and run me again. I am stopping in case you still have something valuable there.' - exit 1 - fi -else - if test -d "$dotest" - then - die "previous rebase directory $dotest still exists." \ - 'Try git rebase (--continue | --abort | --skip)' - fi fi -require_clean_work_tree "rebase" "Please commit or stash them." +if test -n "$interactive_rebase" +then + type=interactive + state_dir="$merge_dir" +elif test -n "$do_merge" +then + type=merge + state_dir="$merge_dir" +else + type=am + state_dir="$apply_dir" +fi if test -z "$rebase_root" then - # The upstream head must be given. Make sure it is valid. - upstream_name="$1" - shift + case "$#" in + 0) + if ! upstream_name=$(git rev-parse --symbolic-full-name \ + --verify -q @{upstream} 2>/dev/null) + then + . git-parse-remote + error_on_missing_default_upstream "rebase" "rebase" \ + "against" "git rebase <upstream branch>" + fi + ;; + *) upstream_name="$1" + shift + ;; + esac upstream=`git rev-parse --verify "${upstream_name}^0"` || die "invalid upstream $upstream_name" - unset root_flag upstream_arg="$upstream_name" else - test -z "$newbase" && die "--root must be used with --onto" + test -z "$onto" && die "You must specify --onto when using --root" unset upstream_name unset upstream - root_flag="--root" - upstream_arg="$root_flag" + upstream_arg=--root fi # Make sure the branch to rebase onto is valid. -onto_name=${newbase-"$upstream_name"} +onto_name=${onto-"$upstream_name"} case "$onto_name" in *...*) if left=${onto_name%...*} right=${onto_name#*...} && @@ -456,13 +415,11 @@ case "$onto_name" in fi ;; *) - onto=$(git rev-parse --verify "${onto_name}^0") || exit + onto=$(git rev-parse --verify "${onto_name}^0") || + die "Does not point to a valid commit: $1" ;; esac -# If a hook exists, give it a chance to interrupt -run_pre_rebase_hook "$upstream_arg" "$@" - # If the branch to rebase is given, that is the branch we will rebase # $branch_name -- branch being rebased, or HEAD (already detached) # $orig_head -- commit object name of tip of the branch before rebasing @@ -475,10 +432,10 @@ case "$#" in switch_to="$1" if git show-ref --verify --quiet -- "refs/heads/$1" && - branch=$(git rev-parse -q --verify "refs/heads/$1") + orig_head=$(git rev-parse -q --verify "refs/heads/$1") then head_name="refs/heads/$1" - elif branch=$(git rev-parse -q --verify "$1") + elif orig_head=$(git rev-parse -q --verify "$1") then head_name="detached HEAD" else @@ -496,20 +453,23 @@ case "$#" in head_name="detached HEAD" branch_name=HEAD ;# detached fi - branch=$(git rev-parse --verify "${branch_name}^0") || exit + orig_head=$(git rev-parse --verify "${branch_name}^0") || exit ;; esac -orig_head=$branch -# Now we are rebasing commits $upstream..$branch (or with --root, -# everything leading up to $branch) on top of $onto +require_clean_work_tree "rebase" "Please commit or stash them." + +# Now we are rebasing commits $upstream..$orig_head (or with --root, +# everything leading up to $orig_head) on top of $onto # Check if we are already based on $onto with linear history, -# but this should be done only when upstream and onto are the same. -mb=$(git merge-base "$onto" "$branch") -if test "$upstream" = "$onto" && test "$mb" = "$onto" && +# but this should be done only when upstream and onto are the same +# and if this is not an interactive rebase. +mb=$(git merge-base "$onto" "$orig_head") +if test "$type" != interactive && test "$upstream" = "$onto" && + test "$mb" = "$onto" && # linear history? - ! (git rev-list --parents "$onto".."$branch" | sane_grep " .* ") > /dev/null + ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null then if test -z "$force_rebase" then @@ -522,10 +482,8 @@ then fi fi -# Detach HEAD and reset the tree -say "First, rewinding head to replay your work on top of it..." -git checkout -q "$onto^0" || die "could not detach HEAD" -git update-ref ORIG_HEAD $branch +# If a hook exists, give it a chance to interrupt +run_pre_rebase_hook "$upstream_arg" "$@" if test -n "$diffstat" then @@ -537,9 +495,16 @@ then GIT_PAGER='' git diff --stat --summary "$mb" "$onto" fi +test "$type" = interactive && run_specific_rebase + +# Detach HEAD and reset the tree +say "First, rewinding head to replay your work on top of it..." +git checkout -q "$onto^0" || die "could not detach HEAD" +git update-ref ORIG_HEAD $orig_head + # If the $onto is a proper descendant of the tip of the branch, then # we just fast-forwarded. -if test "$mb" = "$branch" +if test "$mb" = "$orig_head" then say "Fast-forwarded $branch_name to $onto_name." move_to_original_branch @@ -553,51 +518,4 @@ else revisions="$upstream..$orig_head" fi -if test -z "$do_merge" -then - git format-patch -k --stdout --full-index --ignore-if-in-upstream \ - --src-prefix=a/ --dst-prefix=b/ \ - --no-renames $root_flag "$revisions" | - git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" && - move_to_original_branch - ret=$? - test 0 != $ret -a -d "$GIT_DIR"/rebase-apply && - echo $head_name > "$GIT_DIR"/rebase-apply/head-name && - echo $onto > "$GIT_DIR"/rebase-apply/onto && - echo $orig_head > "$GIT_DIR"/rebase-apply/orig-head && - echo "$GIT_QUIET" > "$GIT_DIR"/rebase-apply/quiet - exit $ret -fi - -# start doing a rebase with git-merge -# this is rename-aware if the recursive (default) strategy is used - -mkdir -p "$dotest" -echo "$onto" > "$dotest/onto" -echo "$onto_name" > "$dotest/onto_name" -prev_head=$orig_head -echo "$prev_head" > "$dotest/prev_head" -echo "$orig_head" > "$dotest/orig-head" -echo "$head_name" > "$dotest/head-name" -echo "$GIT_QUIET" > "$dotest/quiet" - -msgnum=0 -for cmt in `git rev-list --reverse --no-merges "$revisions"` -do - msgnum=$(($msgnum + 1)) - echo "$cmt" > "$dotest/cmt.$msgnum" -done - -echo 1 >"$dotest/msgnum" -echo $msgnum >"$dotest/end" - -end=$msgnum -msgnum=1 - -while test "$msgnum" -le "$end" -do - call_merge "$msgnum" - continue_merge -done - -finish_rb_merge +run_specific_rebase diff --git a/git-send-email.perl b/git-send-email.perl index 76565de2ee..98ab33aae7 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1091,7 +1091,7 @@ X-Mailer: git-send-email $gitversion "VALUES: server=$smtp_server ", "encryption=$smtp_encryption ", "hello=$smtp_domain", - defined $smtp_server_port ? "port=$smtp_server_port" : ""; + defined $smtp_server_port ? " port=$smtp_server_port" : ""; } if (defined $smtp_authuser) { diff --git a/git-submodule.sh b/git-submodule.sh index b010a67309..bf110e9cb7 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -8,7 +8,7 @@ dashless=$(basename "$0" | sed -e 's/-/ /') USAGE="[--quiet] add [-b branch] [-f|--force] [--reference <repository>] [--] <repository> [<path>] or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...] or: $dashless [--quiet] init [--] [<path>...] - or: $dashless [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...] + or: $dashless [--quiet] update [--init] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...] or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...] or: $dashless [--quiet] foreach [--recursive] <command> or: $dashless [--quiet] sync [--] [<path>...]" @@ -402,6 +402,9 @@ cmd_update() -N|--no-fetch) nofetch=1 ;; + -f|--force) + force=$1 + ;; -r|--rebase) update="rebase" ;; @@ -480,10 +483,11 @@ cmd_update() if test "$subsha1" != "$sha1" then - force= - if test -z "$subsha1" + subforce=$force + # If we don't already have a -f flag and the submodule has never been checked out + if test -z "$subsha1" -a -z "$force" then - force="-f" + subforce="-f" fi if test -z "$nofetch" @@ -515,7 +519,7 @@ cmd_update() msg="merged in" ;; *) - command="git checkout $force -q" + command="git checkout $subforce -q" action="checkout" msg="checked out" ;; @@ -179,6 +179,8 @@ static int handle_alias(int *argcp, const char ***argv) if (alias_string[0] == '!') { const char **alias_argv; int argc = *argcp, i; + struct strbuf sb = STRBUF_INIT; + const char *env[2]; commit_pager_choice(); @@ -189,7 +191,13 @@ static int handle_alias(int *argcp, const char ***argv) alias_argv[i] = (*argv)[i]; alias_argv[argc] = NULL; - ret = run_command_v_opt(alias_argv, RUN_USING_SHELL); + strbuf_addstr(&sb, "GIT_PREFIX="); + if (subdir) + strbuf_addstr(&sb, subdir); + env[0] = sb.buf; + env[1] = NULL; + ret = run_command_v_opt_cd_env(alias_argv, RUN_USING_SHELL, NULL, env); + strbuf_release(&sb); if (ret >= 0) /* normal exit */ exit(ret); diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index ee69ea683a..f8db40a1c2 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -186,7 +186,7 @@ our %known_snapshot_formats = ( 'type' => 'application/x-gzip', 'suffix' => '.tar.gz', 'format' => 'tar', - 'compressor' => ['gzip']}, + 'compressor' => ['gzip', '-n']}, 'tbz2' => { 'display' => 'tar.bz2', @@ -536,6 +536,7 @@ struct active_request_slot *get_active_slot(void) curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL); + curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, NULL); curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0); curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); diff --git a/list-objects.c b/list-objects.c index 838b6a732e..0fb44e7ed7 100644 --- a/list-objects.c +++ b/list-objects.c @@ -68,7 +68,7 @@ static void process_tree(struct rev_info *revs, struct tree_desc desc; struct name_entry entry; struct name_path me; - int all_interesting = (revs->diffopt.pathspec.nr == 0); + int match = revs->diffopt.pathspec.nr == 0 ? 2 : 0; int baselen = base->len; if (!revs->tree_objects) @@ -85,7 +85,7 @@ static void process_tree(struct rev_info *revs, me.elem = name; me.elem_len = strlen(name); - if (!all_interesting) { + if (!match) { strbuf_addstr(base, name); if (base->len) strbuf_addch(base, '/'); @@ -94,17 +94,13 @@ static void process_tree(struct rev_info *revs, init_tree_desc(&desc, tree->buffer, tree->size); while (tree_entry(&desc, &entry)) { - if (!all_interesting) { - int showit = tree_entry_interesting(&entry, - base, 0, - &revs->diffopt.pathspec); - - if (showit < 0) + if (match != 2) { + match = tree_entry_interesting(&entry, base, 0, + &revs->diffopt.pathspec); + if (match < 0) break; - else if (!showit) + if (match == 0) continue; - else if (showit == 2) - all_interesting = 1; } if (S_ISDIR(entry.mode)) diff --git a/merge-recursive.c b/merge-recursive.c index af131508ec..ecb1806cad 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -22,11 +22,6 @@ #include "dir.h" #include "submodule.h" -static const char rename_limit_advice[] = -"inexact rename detection was skipped because there were too many\n" -" files. You may want to set your merge.renamelimit variable to at least\n" -" %d and retry this merge."; - static struct tree *shift_tree_object(struct tree *one, struct tree *two, const char *subtree_shift) { @@ -278,7 +273,9 @@ static int save_files_dirs(const unsigned char *sha1, static int get_files_dirs(struct merge_options *o, struct tree *tree) { int n; - if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs, o)) + struct pathspec match_all; + init_pathspec(&match_all, NULL); + if (read_tree_recursive(tree, "", 0, 0, &match_all, save_files_dirs, o)) return 0; n = o->current_file_set.nr + o->current_directory_set.nr; return n; @@ -344,10 +341,11 @@ static void make_room_for_directories_of_df_conflicts(struct merge_options *o, * make room for the corresponding directory. Such paths will * later be processed in process_df_entry() at the end. If * the corresponding directory ends up being removed by the - * merge, then the file will be reinstated at that time; - * otherwise, if the file is not supposed to be removed by the - * merge, the contents of the file will be placed in another - * unique filename. + * merge, then the file will be reinstated at that time + * (albeit with a different timestamp!); otherwise, if the + * file is not supposed to be removed by the merge, the + * contents of the file will be placed in another unique + * filename. * * NOTE: This function relies on the fact that entries for a * D/F conflict will appear adjacent in the index, with the @@ -358,6 +356,13 @@ static void make_room_for_directories_of_df_conflicts(struct merge_options *o, int last_len = 0; int i; + /* + * Do not do any of this crazyness during the recursive; we don't + * even write anything to the working tree! + */ + if (o->call_depth) + return; + for (i = 0; i < entries->nr; i++) { const char *path = entries->items[i].string; int len = strlen(path); @@ -1260,9 +1265,13 @@ static int merge_content(struct merge_options *o, } if (mfi.clean && !df_conflict_remains && - sha_eq(mfi.sha, a_sha) && mfi.mode == a.mode) + sha_eq(mfi.sha, a_sha) && mfi.mode == a.mode && + !o->call_depth && !lstat(path, &st)) { output(o, 3, "Skipped %s (merged same as existing)", path); - else + add_cacheinfo(mfi.mode, mfi.sha, path, + 0 /*stage*/, 1 /*refresh*/, 0 /*options*/); + return mfi.clean; + } else output(o, 2, "Auto-merging %s", path); if (!mfi.clean) { @@ -1652,8 +1661,9 @@ int merge_recursive(struct merge_options *o, commit_list_insert(h2, &(*result)->parents->next); } flush_output(o); - if (o->needed_rename_limit) - warning(rename_limit_advice, o->needed_rename_limit); + if (show(o, 2)) + diff_warn_rename_limit("merge.renamelimit", + o->needed_rename_limit, 0); return clean; } @@ -1053,7 +1053,8 @@ void init_display_notes(struct display_notes_opt *opt) assert(!display_notes_trees); - if (!opt || !opt->suppress_default_notes) { + if (!opt || opt->use_default_notes > 0 || + (opt->use_default_notes == -1 && !opt->extra_notes_refs.nr)) { string_list_append(&display_notes_refs, default_notes_ref()); display_ref_env = getenv(GIT_NOTES_DISPLAY_REF_ENVIRONMENT); if (display_ref_env) { @@ -1066,9 +1067,9 @@ void init_display_notes(struct display_notes_opt *opt) git_config(notes_display_config, &load_config_refs); - if (opt && opt->extra_notes_refs) { + if (opt) { struct string_list_item *item; - for_each_string_list_item(item, opt->extra_notes_refs) + for_each_string_list_item(item, &opt->extra_notes_refs) string_list_add_refs_by_glob(&display_notes_refs, item->string); } @@ -1285,3 +1286,13 @@ int copy_note(struct notes_tree *t, return 0; } + +void expand_notes_ref(struct strbuf *sb) +{ + if (!prefixcmp(sb->buf, "refs/notes/")) + return; /* we're happy */ + else if (!prefixcmp(sb->buf, "notes/")) + strbuf_insert(sb, 0, "refs/", 5); + else + strbuf_insert(sb, 0, "refs/notes/", 11); +} @@ -1,6 +1,8 @@ #ifndef NOTES_H #define NOTES_H +#include "string-list.h" + /* * Function type for combining two notes annotating the same object. * @@ -256,8 +258,8 @@ void format_note(struct notes_tree *t, const unsigned char *object_sha1, struct string_list; struct display_notes_opt { - unsigned int suppress_default_notes:1; - struct string_list *extra_notes_refs; + int use_default_notes; + struct string_list extra_notes_refs; }; /* @@ -307,4 +309,7 @@ void string_list_add_refs_by_glob(struct string_list *list, const char *glob); void string_list_add_refs_from_colon_sep(struct string_list *list, const char *globs); +/* Expand inplace a note ref like "foo" or "notes/foo" into "refs/notes/foo" */ +void expand_notes_ref(struct strbuf *sb); + #endif @@ -208,6 +208,58 @@ int has_non_ascii(const char *s) return 0; } +static int is_rfc822_special(char ch) +{ + switch (ch) { + case '(': + case ')': + case '<': + case '>': + case '[': + case ']': + case ':': + case ';': + case '@': + case ',': + case '.': + case '"': + case '\\': + return 1; + default: + return 0; + } +} + +static int has_rfc822_specials(const char *s, int len) +{ + int i; + for (i = 0; i < len; i++) + if (is_rfc822_special(s[i])) + return 1; + return 0; +} + +static void add_rfc822_quoted(struct strbuf *out, const char *s, int len) +{ + int i; + + /* just a guess, we may have to also backslash-quote */ + strbuf_grow(out, len + 2); + + strbuf_addch(out, '"'); + for (i = 0; i < len; i++) { + switch (s[i]) { + case '"': + case '\\': + strbuf_addch(out, '\\'); + /* fall through */ + default: + strbuf_addch(out, s[i]); + } + } + strbuf_addch(out, '"'); +} + static int is_rfc2047_special(char ch) { return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_')); @@ -287,13 +339,29 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, if (fmt == CMIT_FMT_EMAIL) { char *name_tail = strchr(line, '<'); int display_name_length; + int final_line; if (!name_tail) return; while (line < name_tail && isspace(name_tail[-1])) name_tail--; display_name_length = name_tail - line; strbuf_addstr(sb, "From: "); - add_rfc2047(sb, line, display_name_length, encoding); + if (!has_rfc822_specials(line, display_name_length)) { + add_rfc2047(sb, line, display_name_length, encoding); + } else { + struct strbuf quoted = STRBUF_INIT; + add_rfc822_quoted("ed, line, display_name_length); + add_rfc2047(sb, quoted.buf, quoted.len, encoding); + strbuf_release("ed); + } + for (final_line = 0; final_line < sb->len; final_line++) + if (sb->buf[sb->len - final_line - 1] == '\n') + break; + if (namelen - display_name_length + final_line > 78) { + strbuf_addch(sb, '\n'); + if (!isspace(name_tail[0])) + strbuf_addch(sb, ' '); + } strbuf_add(sb, name_tail, namelen - display_name_length); strbuf_addch(sb, '\n'); } else { diff --git a/revision.c b/revision.c index 0f38364cf3..541f09e218 100644 --- a/revision.c +++ b/revision.c @@ -955,6 +955,8 @@ void init_revisions(struct rev_info *revs, const char *prefix) revs->diffopt.prefix = prefix; revs->diffopt.prefix_length = strlen(prefix); } + + revs->notes_opt.use_default_notes = -1; } static void add_pending_commit_list(struct rev_info *revs, @@ -1365,32 +1367,39 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->verbose_header = 1; revs->pretty_given = 1; get_commit_format(arg+9, revs); - } else if (!strcmp(arg, "--show-notes")) { + } else if (!strcmp(arg, "--show-notes") || !strcmp(arg, "--notes")) { revs->show_notes = 1; revs->show_notes_given = 1; - } else if (!prefixcmp(arg, "--show-notes=")) { + revs->notes_opt.use_default_notes = 1; + } else if (!prefixcmp(arg, "--show-notes=") || + !prefixcmp(arg, "--notes=")) { struct strbuf buf = STRBUF_INIT; revs->show_notes = 1; revs->show_notes_given = 1; - if (!revs->notes_opt.extra_notes_refs) - revs->notes_opt.extra_notes_refs = xcalloc(1, sizeof(struct string_list)); - if (!prefixcmp(arg+13, "refs/")) - /* happy */; - else if (!prefixcmp(arg+13, "notes/")) - strbuf_addstr(&buf, "refs/"); + if (!prefixcmp(arg, "--show-notes")) { + if (revs->notes_opt.use_default_notes < 0) + revs->notes_opt.use_default_notes = 1; + strbuf_addstr(&buf, arg+13); + } else - strbuf_addstr(&buf, "refs/notes/"); - strbuf_addstr(&buf, arg+13); - string_list_append(revs->notes_opt.extra_notes_refs, + strbuf_addstr(&buf, arg+8); + expand_notes_ref(&buf); + string_list_append(&revs->notes_opt.extra_notes_refs, strbuf_detach(&buf, NULL)); } else if (!strcmp(arg, "--no-notes")) { revs->show_notes = 0; revs->show_notes_given = 1; + revs->notes_opt.use_default_notes = -1; + /* we have been strdup'ing ourselves, so trick + * string_list into free()ing strings */ + revs->notes_opt.extra_notes_refs.strdup_strings = 1; + string_list_clear(&revs->notes_opt.extra_notes_refs, 0); + revs->notes_opt.extra_notes_refs.strdup_strings = 0; } else if (!strcmp(arg, "--standard-notes")) { revs->show_notes_given = 1; - revs->notes_opt.suppress_default_notes = 0; + revs->notes_opt.use_default_notes = 1; } else if (!strcmp(arg, "--no-standard-notes")) { - revs->notes_opt.suppress_default_notes = 1; + revs->notes_opt.use_default_notes = 0; } else if (!strcmp(arg, "--oneline")) { revs->verbose_header = 1; get_commit_format("oneline", revs); diff --git a/revision.h b/revision.h index 9fd8f3016f..bca9947977 100644 --- a/revision.h +++ b/revision.h @@ -141,6 +141,7 @@ struct rev_info { /* commit counts */ int count_left; int count_right; + int count_same; }; #define REV_TREE_SAME 0 @@ -325,6 +325,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv, const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT); const char *worktree; char *gitfile; + int offset; if (PATH_MAX - 40 < strlen(gitdirenv)) die("'$%s' too big", GIT_DIR_ENVIRONMENT); @@ -390,15 +391,15 @@ static const char *setup_explicit_git_dir(const char *gitdirenv, return NULL; } - if (!prefixcmp(cwd, worktree) && - cwd[strlen(worktree)] == '/') { /* cwd inside worktree */ + offset = dir_inside_of(cwd, worktree); + if (offset >= 0) { /* cwd inside worktree? */ set_git_dir(real_path(gitdirenv)); if (chdir(worktree)) die_errno("Could not chdir to '%s'", worktree); cwd[len++] = '/'; cwd[len] = '\0'; free(gitfile); - return cwd + strlen(worktree) + 1; + return cwd + offset; } /* cwd outside worktree */ @@ -137,6 +137,8 @@ int main(int argc, char **argv) int devnull_fd; int count; + git_extract_argv0_path(argv[0]); + /* * Always open file descriptors 0/1/2 to avoid clobbering files * in die(). It also avoids not messing up when the pipes are @@ -3,8 +3,6 @@ /* See Documentation/technical/api-strbuf.txt */ -#include <assert.h> - extern char strbuf_slopbuf[]; struct strbuf { size_t alloc; @@ -33,9 +31,8 @@ static inline size_t strbuf_avail(const struct strbuf *sb) { extern void strbuf_grow(struct strbuf *, size_t); static inline void strbuf_setlen(struct strbuf *sb, size_t len) { - if (!sb->alloc) - strbuf_grow(sb, 0); - assert(len < sb->alloc); + if (len > (sb->alloc ? sb->alloc - 1 : 0)) + die("BUG: strbuf_setlen() beyond buffer"); sb->len = len; sb->buf[len] = '\0'; } @@ -379,7 +379,7 @@ library for your script to use. - test_expect_success [<prereq>] <message> <script> - Usually takes two strings as parameter, and evaluates the + Usually takes two strings as parameters, and evaluates the <script>. If it yields success, test is considered successful. <message> should state what it is testing. @@ -390,7 +390,7 @@ library for your script to use. 'tree=$(git-write-tree)' If you supply three parameters the first will be taken to be a - prerequisite, see the test_set_prereq and test_have_prereq + prerequisite; see the test_set_prereq and test_have_prereq documentation below: test_expect_success TTY 'git --paginate rev-list uses a pager' \ @@ -446,7 +446,7 @@ library for your script to use. Merges the given rev using the given message. Like test_commit, creates a tag and calls test_tick before committing. - - test_set_prereq SOME_PREREQ + - test_set_prereq <prereq> Set a test prerequisite to be used later with test_have_prereq. The test-lib will set some prerequisites for you, see the @@ -456,7 +456,7 @@ library for your script to use. test_have_prereq directly, or the three argument invocation of test_expect_success and test_expect_failure. - - test_have_prereq SOME PREREQ + - test_have_prereq <prereq> Check if we have a prerequisite previously set with test_set_prereq. The most common use of this directly is to skip @@ -526,12 +526,13 @@ library for your script to use. Check whether a file has the length it is expected to. - - test_path_is_file <file> [<diagnosis>] - test_path_is_dir <dir> [<diagnosis>] + - test_path_is_file <path> [<diagnosis>] + test_path_is_dir <path> [<diagnosis>] test_path_is_missing <path> [<diagnosis>] - Check whether a file/directory exists or doesn't. <diagnosis> will - be displayed if the test fails. + Check if the named path is a file, if the named path is a + directory, or if the named path does not exist, respectively, + and fail otherwise, showing the <diagnosis> text. - test_when_finished <script> diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh index d34208cc27..c56a77d237 100644 --- a/t/annotate-tests.sh +++ b/t/annotate-tests.sh @@ -1,5 +1,5 @@ # This file isn't used as a test script directly, instead it is -# sourced from t8001-annotate.sh and t8001-blame.sh. +# sourced from t8001-annotate.sh and t8002-blame.sh. check_count () { head= @@ -124,3 +124,14 @@ test_expect_success \ test_expect_success \ 'some edit' \ 'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1' + +test_expect_success \ + 'an obfuscated email added' \ + 'echo "No robots allowed" > file.new && + cat file >> file.new && + mv file.new file && + GIT_AUTHOR_NAME="E" GIT_AUTHOR_EMAIL="E at test dot git" git commit -a -m "norobots"' + +test_expect_success \ + 'obfuscated email parsed' \ + 'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1' diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index d3829b8d0a..b8996a373a 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -157,8 +157,7 @@ test_http_push_nonff() { grep "^ ! \[rejected\][ ]*$BRANCH -> $BRANCH (non-fast-forward)$" output ' - test_expect_success C_LOCALE_OUTPUT 'non-fast-forward push shows help message' ' - grep "To prevent you from losing history, non-fast-forward updates were rejected" \ - output + test_expect_success 'non-fast-forward push shows help message' ' + test_i18ngrep "To prevent you from losing history, non-fast-forward updates were rejected" output ' } diff --git a/t/t0001-init.sh b/t/t0001-init.sh index 54520f6fa6..8106af8fba 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -180,7 +180,7 @@ test_expect_success 'GIT_DIR & GIT_WORK_TREE (2)' ' fi ' -test_expect_success C_LOCALE_OUTPUT 'reinit' ' +test_expect_success 'reinit' ' ( sane_unset GIT_CONFIG GIT_WORK_TREE GIT_CONFIG && @@ -190,11 +190,11 @@ test_expect_success C_LOCALE_OUTPUT 'reinit' ' git init >out1 2>err1 && git init >out2 2>err2 ) && - grep "Initialized empty" again/out1 && - grep "Reinitialized existing" again/out2 && + test_i18ngrep "Initialized empty" again/out1 && + test_i18ngrep "Reinitialized existing" again/out2 && >again/empty && - test_cmp again/empty again/err1 && - test_cmp again/empty again/err2 + test_i18ncmp again/empty again/err1 && + test_i18ncmp again/empty again/err2 ' test_expect_success 'init with --template' ' diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh index 1fd187c5eb..ddc3921ac6 100755 --- a/t/t1020-subdirectory.sh +++ b/t/t1020-subdirectory.sh @@ -118,6 +118,27 @@ test_expect_success 'alias expansion' ' git ss ) ' + +test_expect_success '!alias expansion' ' + pwd >expect && + ( + git config alias.test !pwd && + cd dir && + git test >../actual + ) && + test_cmp expect actual +' + +test_expect_success 'GIT_PREFIX for !alias' ' + printf "dir/" >expect && + ( + git config alias.test "!sh -c \"printf \$GIT_PREFIX\"" && + cd dir && + git test >../actual + ) && + test_cmp expect actual +' + test_expect_success 'no file/rev ambiguity check inside .git' ' git commit -a -m 1 && ( diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh index 3264fefbad..5e29e13782 100755 --- a/t/t1200-tutorial.sh +++ b/t/t1200-tutorial.sh @@ -166,8 +166,8 @@ test_expect_success 'git resolve' ' -e "s/^Fast[- ]forward /FASTFORWARD /" >resolve.output ' -test_expect_success C_LOCALE_OUTPUT 'git resolve output' ' - test_cmp resolve.expect resolve.output +test_expect_success 'git resolve output' ' + test_i18ncmp resolve.expect resolve.output ' cat > show-branch2.expect << EOF diff --git a/t/t2019-checkout-ambiguous-ref.sh b/t/t2019-checkout-ambiguous-ref.sh index cc34e5535b..b99d5192a9 100755 --- a/t/t2019-checkout-ambiguous-ref.sh +++ b/t/t2019-checkout-ambiguous-ref.sh @@ -29,9 +29,9 @@ test_expect_success 'checkout chooses branch over tag' ' test_cmp expect file ' -test_expect_success C_LOCALE_OUTPUT 'checkout reports switch to branch' ' - grep "Switched to branch" stderr && - ! grep "^HEAD is now at" stderr +test_expect_success 'checkout reports switch to branch' ' + test_i18ngrep "Switched to branch" stderr && + test_i18ngrep ! "^HEAD is now at" stderr ' test_expect_success 'checkout vague ref succeeds' ' @@ -51,9 +51,9 @@ test_expect_success VAGUENESS_SUCCESS 'checkout chooses branch over tag' ' test_cmp expect file ' -test_expect_success VAGUENESS_SUCCESS,C_LOCALE_OUTPUT 'checkout reports switch to branch' ' - grep "Switched to branch" stderr && - ! grep "^HEAD is now at" stderr +test_expect_success VAGUENESS_SUCCESS 'checkout reports switch to branch' ' + test_i18ngrep "Switched to branch" stderr && + test_i18ngrep ! "^HEAD is now at" stderr ' test_done diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh index 569b27fe8d..2366f0f414 100755 --- a/t/t2020-checkout-detach.sh +++ b/t/t2020-checkout-detach.sh @@ -13,10 +13,10 @@ check_not_detached () { ORPHAN_WARNING='you are leaving .* commit.*behind' check_orphan_warning() { - grep "$ORPHAN_WARNING" "$1" + test_i18ngrep "$ORPHAN_WARNING" "$1" } check_no_orphan_warning() { - ! grep "$ORPHAN_WARNING" "$1" + test_i18ngrep ! "$ORPHAN_WARNING" "$1" } reset () { @@ -108,21 +108,30 @@ test_expect_success 'checkout warns on orphan commits' ' echo content >orphan && git add orphan && git commit -a -m orphan && - git checkout master 2>stderr && + git checkout master 2>stderr +' + +test_expect_success 'checkout warns on orphan commits: output' ' check_orphan_warning stderr ' test_expect_success 'checkout does not warn leaving ref tip' ' reset && git checkout --detach two && - git checkout master 2>stderr && + git checkout master 2>stderr +' + +test_expect_success 'checkout does not warn leaving ref tip' ' check_no_orphan_warning stderr ' test_expect_success 'checkout does not warn leaving reachable commit' ' reset && git checkout --detach HEAD^ && - git checkout master 2>stderr && + git checkout master 2>stderr +' + +test_expect_success 'checkout does not warn leaving reachable commit' ' check_no_orphan_warning stderr ' diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index 856e7da1f2..2d7d3115d5 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -111,7 +111,7 @@ test_expect_success 'touch and then add explicitly' ' ' -test_expect_success C_LOCALE_OUTPUT 'add -n -u should not add but just report' ' +test_expect_success 'add -n -u should not add but just report' ' ( echo "add '\''check'\''" && @@ -124,7 +124,7 @@ test_expect_success C_LOCALE_OUTPUT 'add -n -u should not add but just report' ' after=$(git ls-files -s check top) && test "$before" = "$after" && - test_cmp expect actual + test_i18ncmp expect actual ' diff --git a/t/t2204-add-ignored.sh b/t/t2204-add-ignored.sh index 49753362f0..8340ac2f07 100755 --- a/t/t2204-add-ignored.sh +++ b/t/t2204-add-ignored.sh @@ -34,8 +34,8 @@ do ! test -s out ' - test_expect_success C_LOCALE_OUTPUT "complaints for ignored $i output" ' - grep -e "Use -f if" err + test_expect_success "complaints for ignored $i output" ' + test_i18ngrep -e "Use -f if" err ' test_expect_success "complaints for ignored $i with unignored file" ' @@ -44,8 +44,8 @@ do git ls-files "$i" >out && ! test -s out ' - test_expect_success C_LOCALE_OUTPUT "complaints for ignored $i with unignored file output" ' - grep -e "Use -f if" err + test_expect_success "complaints for ignored $i with unignored file output" ' + test_i18ngrep -e "Use -f if" err ' done @@ -61,10 +61,10 @@ do ) ' - test_expect_success C_LOCALE_OUTPUT "complaints for ignored $i in dir output" ' + test_expect_success "complaints for ignored $i in dir output" ' ( cd dir && - grep -e "Use -f if" err + test_i18ngrep -e "Use -f if" err ) ' done @@ -81,10 +81,10 @@ do ) ' - test_expect_success C_LOCALE_OUTPUT "complaints for ignored $i in sub output" ' + test_expect_success "complaints for ignored $i in sub output" ' ( cd sub && - grep -e "Use -f if" err + test_i18ngrep -e "Use -f if" err ) ' done diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh index 806fdccce1..0c02d56952 100755 --- a/t/t3030-merge-recursive.sh +++ b/t/t3030-merge-recursive.sh @@ -312,20 +312,20 @@ test_expect_success 'merge-recursive result' ' ' -test_expect_success C_LOCALE_OUTPUT 'fail if the index has unresolved entries' ' +test_expect_success 'fail if the index has unresolved entries' ' rm -fr [abcd] && git checkout -f "$c1" && test_must_fail git merge "$c5" && test_must_fail git merge "$c5" 2> out && - grep "not possible because you have unmerged files" out && + test_i18ngrep "not possible because you have unmerged files" out && git add -u && test_must_fail git merge "$c5" 2> out && - grep "You have not concluded your merge" out && + test_i18ngrep "You have not concluded your merge" out && rm -f .git/MERGE_HEAD && test_must_fail git merge "$c5" 2> out && - grep "Your local changes to the following files would be overwritten by merge:" out + test_i18ngrep "Your local changes to the following files would be overwritten by merge:" out ' test_expect_success 'merge-recursive remove conflict' ' diff --git a/t/t3102-ls-tree-wildcards.sh b/t/t3102-ls-tree-wildcards.sh new file mode 100755 index 0000000000..f2b2a524d9 --- /dev/null +++ b/t/t3102-ls-tree-wildcards.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +test_description='ls-tree with(out) wildcards' + +. ./test-lib.sh + +test_expect_success 'setup' ' + mkdir a aa "a*" && + touch a/one aa/two "a*/three" && + git add a/one aa/two "a*/three" && + git commit -m test +' + +test_expect_success 'ls-tree a* matches literally' ' + cat >expected <<EOF && +100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 a*/three +EOF + git ls-tree -r HEAD "a*" >actual && + test_cmp expected actual +' + +test_done diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 286a2a6869..0ce95c04e5 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -203,10 +203,12 @@ test_expect_success 'test deleting branch deletes branch config' \ test -z "$(git config branch.my7.remote)" && test -z "$(git config branch.my7.merge)"' -test_expect_success C_LOCALE_OUTPUT 'test deleting branch without config' \ +test_expect_success 'test deleting branch without config' \ 'git branch my7 s && sha1=$(git rev-parse my7 | cut -c 1-7) && - test "$(git branch -d my7 2>&1)" = "Deleted branch my7 (was $sha1)."' + echo "Deleted branch my7 (was $sha1)." >expect && + git branch -d my7 >actual 2>&1 && + test_i18ncmp expect actual' test_expect_success 'test --track without .fetch entries' \ 'git branch --track my8 && diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index 4ef7d09115..6b7c118e4f 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -72,10 +72,10 @@ cat >expect <<'EOF' branch-two master EOF -test_expect_success C_LOCALE_OUTPUT 'git branch shows detached HEAD properly' ' +test_expect_success 'git branch shows detached HEAD properly' ' git checkout HEAD^0 && git branch >actual && - test_cmp expect actual + test_i18ncmp expect actual ' test_done diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh index 1921ca3a73..28e17c8920 100755 --- a/t/t3301-notes.sh +++ b/t/t3301-notes.sh @@ -101,8 +101,8 @@ test_expect_success 'edit existing notes' ' test_must_fail git notes show HEAD^ ' -test_expect_success 'cannot add note where one exists' ' - ! MSG=b2 git notes add && +test_expect_success 'cannot "git notes add -m" where notes already exists' ' + test_must_fail git notes add -m "b2" && test ! -f .git/NOTES_EDITMSG && test 1 = $(git ls-tree refs/notes/commits | wc -l) && test b3 = $(git notes show) && @@ -110,6 +110,24 @@ test_expect_success 'cannot add note where one exists' ' test_must_fail git notes show HEAD^ ' +test_expect_success 'can overwrite existing note with "git notes add -f -m"' ' + git notes add -f -m "b1" && + test ! -f .git/NOTES_EDITMSG && + test 1 = $(git ls-tree refs/notes/commits | wc -l) && + test b1 = $(git notes show) && + git show HEAD^ && + test_must_fail git notes show HEAD^ +' + +test_expect_success 'add w/no options on existing note morphs into edit' ' + MSG=b2 git notes add && + test ! -f .git/NOTES_EDITMSG && + test 1 = $(git ls-tree refs/notes/commits | wc -l) && + test b2 = $(git notes show) && + git show HEAD^ && + test_must_fail git notes show HEAD^ +' + test_expect_success 'can overwrite existing note with "git notes add -f"' ' MSG=b1 git notes add -f && test ! -f .git/NOTES_EDITMSG && @@ -194,6 +212,13 @@ test_expect_success 'show -F notes' ' test_cmp expect-F output ' +test_expect_success 'Re-adding -F notes without -f fails' ' + echo "zyxxy" > note5 && + test_must_fail git notes add -F note5 && + git log -3 > output && + test_cmp expect-F output +' + cat >expect << EOF commit 15023535574ded8b1a89052b32673f84cf9582b8 tree e070e3af51011e47b183c33adf9736736a525709 @@ -247,6 +272,44 @@ do ' done +test_expect_success 'setup alternate notes ref' ' + git notes --ref=alternate add -m alternate +' + +test_expect_success 'git log --notes shows default notes' ' + git log -1 --notes >output && + grep xyzzy output && + ! grep alternate output +' + +test_expect_success 'git log --notes=X shows only X' ' + git log -1 --notes=alternate >output && + ! grep xyzzy output && + grep alternate output +' + +test_expect_success 'git log --notes --notes=X shows both' ' + git log -1 --notes --notes=alternate >output && + grep xyzzy output && + grep alternate output +' + +test_expect_success 'git log --no-notes resets default state' ' + git log -1 --notes --notes=alternate \ + --no-notes --notes=alternate \ + >output && + ! grep xyzzy output && + grep alternate output +' + +test_expect_success 'git log --no-notes resets ref list' ' + git log -1 --notes --notes=alternate \ + --no-notes --notes \ + >output && + grep xyzzy output && + ! grep alternate output +' + test_expect_success 'create -m notes (setup)' ' : > a5 && git add a5 && diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index 349eebd542..6eaecec906 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -158,15 +158,24 @@ test_expect_success 'Show verbose error when HEAD could not be detached' ' ' rm -f B -test_expect_success 'dump usage when upstream arg is missing' ' - git checkout -b usage topic && - test_must_fail git rebase 2>error1 && - grep "[Uu]sage" error1 && - test_must_fail git rebase --abort 2>error2 && - grep "No rebase in progress" error2 && - test_must_fail git rebase --onto master 2>error3 && - grep "[Uu]sage" error3 && - ! grep "can.t shift" error3 +test_expect_success 'fail when upstream arg is missing and not on branch' ' + git checkout topic && + test_must_fail git rebase >output.out && + grep "You are not currently on a branch" output.out +' + +test_expect_success 'fail when upstream arg is missing and not configured' ' + git checkout -b no-config topic && + test_must_fail git rebase >output.out && + grep "branch.no-config.merge" output.out +' + +test_expect_success 'default to @{upstream} when upstream arg is missing' ' + git checkout -b default topic && + git config branch.default.remote . + git config branch.default.merge refs/heads/master + git rebase && + test "$(git rev-parse default~1)" = "$(git rev-parse master)" ' test_expect_success 'rebase -q is quiet' ' diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh index 64446e3db3..826500bd18 100755 --- a/t/t3403-rebase-skip.sh +++ b/t/t3403-rebase-skip.sh @@ -35,6 +35,11 @@ test_expect_success 'rebase with git am -3 (default)' ' test_must_fail git rebase master ' +test_expect_success 'rebase --skip can not be used with other options' ' + test_must_fail git rebase -v --skip && + test_must_fail git rebase --skip -v +' + test_expect_success 'rebase --skip with am -3' ' git rebase --skip ' diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh index e573dc845b..a6a6c40a98 100755 --- a/t/t3407-rebase-abort.sh +++ b/t/t3407-rebase-abort.sh @@ -84,6 +84,16 @@ testrebase() { test_cmp reflog_before reflog_after && rm reflog_before reflog_after ' + + test_expect_success 'rebase --abort can not be used with other options' ' + cd "$work_dir" && + # Clean up the state from the previous one + git reset --hard pre-rebase && + test_must_fail git rebase$type master && + test_must_fail git rebase -v --abort && + test_must_fail git rebase --abort -v && + git rebase --abort + ' } testrebase "" .git/rebase-apply diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh index 19341e5ca1..08201e2331 100755 --- a/t/t3409-rebase-preserve-merges.sh +++ b/t/t3409-rebase-preserve-merges.sh @@ -27,7 +27,17 @@ export GIT_AUTHOR_EMAIL # \ # B2 <-- origin/topic # -# In both cases, 'topic' is rebased onto 'origin/topic'. +# Clone 3 (no-ff merge): +# +# A1--A2--B3 <-- origin/master +# \ +# B1------M <-- topic +# \ / +# \--A3 <-- topic2 +# \ +# B2 <-- origin/topic +# +# In all cases, 'topic' is rebased onto 'origin/topic'. test_expect_success 'setup for merge-preserving rebase' \ 'echo First > A && @@ -61,6 +71,16 @@ test_expect_success 'setup for merge-preserving rebase' \ git commit -m "Merge origin/master into topic" ) && + git clone ./. clone3 && + ( + cd clone3 && + git checkout -b topic2 origin/topic && + echo Sixth > A && + git commit -a -m "Modify A3" && + git checkout -b topic origin/topic && + git merge --no-ff topic2 + ) && + git checkout topic && echo Fourth >> B && git commit -a -m "Modify B2" @@ -93,4 +113,14 @@ test_expect_success '--continue works after a conflict' ' ) ' +test_expect_success 'rebase -p preserves no-ff merges' ' + ( + cd clone3 && + git fetch && + git rebase -p origin/topic && + test 3 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) && + test 1 = $(git rev-list --all --pretty=oneline | grep "Merge branch" | wc -l) + ) +' + test_done diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh index 3b0d27350e..1e855cdae5 100755 --- a/t/t3418-rebase-continue.sh +++ b/t/t3418-rebase-continue.sh @@ -40,4 +40,59 @@ test_expect_success 'non-interactive rebase --continue works with touched file' git rebase --continue ' +test_expect_success 'rebase --continue can not be used with other options' ' + test_must_fail git rebase -v --continue && + test_must_fail git rebase --continue -v +' + +test_expect_success 'rebase --continue remembers merge strategy and options' ' + rm -fr .git/rebase-* && + git reset --hard commit-new-file-F2-on-topic-branch && + test_commit "commit-new-file-F3-on-topic-branch" F3 32 && + test_when_finished "rm -fr test-bin funny.was.run" && + mkdir test-bin && + cat >test-bin/git-merge-funny <<-EOF + #!$SHELL_PATH + case "\$1" in --opt) ;; *) exit 2 ;; esac + shift && + >funny.was.run && + exec git merge-recursive "\$@" + EOF + chmod +x test-bin/git-merge-funny && + ( + PATH=./test-bin:$PATH + test_must_fail git rebase -s funny -Xopt master topic + ) && + test -f funny.was.run && + rm funny.was.run && + echo "Resolved" >F2 && + git add F2 && + ( + PATH=./test-bin:$PATH + git rebase --continue + ) && + test -f funny.was.run +' + +test_expect_success 'rebase --continue remembers --rerere-autoupdate' ' + rm -fr .git/rebase-* && + git reset --hard commit-new-file-F3-on-topic-branch && + git checkout master + test_commit "commit-new-file-F3" F3 3 && + git config rerere.enabled true && + test_must_fail git rebase -m master topic && + echo "Resolved" >F2 && + git add F2 && + test_must_fail git rebase --continue && + echo "Resolved" >F3 && + git add F3 && + git rebase --continue && + git reset --hard topic@{1} && + test_must_fail git rebase -m --rerere-autoupdate master && + test "$(cat F2)" = "Resolved" && + test_must_fail git rebase --continue && + test "$(cat F3)" = "Resolved" && + git rebase --continue +' + test_done diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh index 753a6c972c..595d2ff990 100755 --- a/t/t3501-revert-cherry-pick.sh +++ b/t/t3501-revert-cherry-pick.sh @@ -91,12 +91,12 @@ test_expect_success 'cherry-pick on stat-dirty working tree' ' ) ' -test_expect_success C_LOCALE_OUTPUT 'revert forbidden on dirty working tree' ' +test_expect_success 'revert forbidden on dirty working tree' ' echo content >extra_file && git add extra_file && test_must_fail git revert HEAD 2>errors && - grep "Your local changes would be overwritten by " errors + test_i18ngrep "Your local changes would be overwritten by " errors ' diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh index c0c8330c20..212ec54aaf 100755 --- a/t/t3507-cherry-pick-conflict.sh +++ b/t/t3507-cherry-pick-conflict.sh @@ -44,7 +44,7 @@ test_expect_success 'failed cherry-pick does not advance HEAD' ' test "$head" = "$newhead" ' -test_expect_success C_LOCALE_OUTPUT 'advice from failed cherry-pick' " +test_expect_success 'advice from failed cherry-pick' " pristine_detach initial && picked=\$(git rev-parse --short picked) && @@ -56,7 +56,7 @@ test_expect_success C_LOCALE_OUTPUT 'advice from failed cherry-pick' " EOF test_must_fail git cherry-pick picked 2>actual && - test_cmp expected actual + test_i18ncmp expected actual " test_expect_success 'failed cherry-pick sets CHERRY_PICK_HEAD' ' diff --git a/t/t3700-add.sh b/t/t3700-add.sh index 7de42faf48..575d9508a0 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -271,9 +271,9 @@ test_expect_success 'git add --dry-run of non-existing file' " test_must_fail git add --dry-run track-this ignored-file >actual 2>&1 " -test_expect_success C_LOCALE_OUTPUT 'git add --dry-run of an existing file output' " +test_expect_success 'git add --dry-run of an existing file output' " echo \"fatal: pathspec 'ignored-file' did not match any files\" >expect && - test_cmp expect actual + test_i18ncmp expect actual " cat >expect.err <<\EOF @@ -290,9 +290,9 @@ test_expect_success 'git add --dry-run --ignore-missing of non-existing file' ' test_must_fail git add --dry-run --ignore-missing track-this ignored-file >actual.out 2>actual.err ' -test_expect_success C_LOCALE_OUTPUT 'git add --dry-run --ignore-missing of non-existing file output' ' - test_cmp expect.out actual.out && - test_cmp expect.err actual.err +test_expect_success 'git add --dry-run --ignore-missing of non-existing file output' ' + test_i18ncmp expect.out actual.out && + test_i18ncmp expect.err actual.err ' test_done diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index d6327e7c74..fdcbe2e736 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -82,10 +82,9 @@ EOF ' test_expect_success PERL 'setup fake editor' ' - cat >fake_editor.sh <<EOF - EOF + >fake_editor.sh && chmod a+x fake_editor.sh && - test_set_editor "$(pwd)/fake_editor.sh" && + test_set_editor "$(pwd)/fake_editor.sh" ' test_expect_success PERL 'dummy edit works' ' diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh index cad85450b7..844277cfa6 100755 --- a/t/t4001-diff-rename.sh +++ b/t/t4001-diff-rename.sh @@ -64,17 +64,42 @@ test_expect_success \ 'validate the output.' \ 'compare_diff_patch current expected' -test_expect_success C_LOCALE_OUTPUT 'favour same basenames over different ones' ' +test_expect_success 'favour same basenames over different ones' ' cp path1 another-path && git add another-path && git commit -m 1 && git rm path1 && mkdir subdir && git mv another-path subdir/path1 && - git status | grep "renamed: .*path1 -> subdir/path1"' + git status | test_i18ngrep "renamed: .*path1 -> subdir/path1"' -test_expect_success C_LOCALE_OUTPUT 'favour same basenames even with minor differences' ' +test_expect_success 'favour same basenames even with minor differences' ' git show HEAD:path1 | sed "s/15/16/" > subdir/path1 && - git status | grep "renamed: .*path1 -> subdir/path1"' + git status | test_i18ngrep "renamed: .*path1 -> subdir/path1"' + +test_expect_success 'setup for many rename source candidates' ' + git reset --hard && + for i in 0 1 2 3 4 5 6 7 8 9; + do + for j in 0 1 2 3 4 5 6 7 8 9; + do + echo "$i$j" >"path$i$j" + done + done && + git add "path??" && + test_tick && + git commit -m "hundred" && + (cat path1; echo new) >new-path && + echo old >>path1 && + git add new-path path1 && + git diff -l 4 -C -C --cached --name-status >actual 2>actual.err && + sed -e "s/^\([CM]\)[0-9]* /\1 /" actual >actual.munged && + cat >expect <<-EOF && + C path1 new-path + M path1 + EOF + test_cmp expect actual.munged && + grep warning actual.err +' test_done diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 5daa0f2a0c..93a6f20871 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -80,18 +80,31 @@ test_expect_success setup ' git config log.showroot false && git commit --amend && + + GIT_AUTHOR_DATE="2006-06-26 00:06:00 +0000" && + GIT_COMMITTER_DATE="2006-06-26 00:06:00 +0000" && + export GIT_AUTHOR_DATE GIT_COMMITTER_DATE && + git checkout -b rearrange initial && + for i in B A; do echo $i; done >dir/sub && + git add dir/sub && + git commit -m "Rearranged lines in dir/sub" && + git checkout master && + git show-branch ' : <<\EOF ! [initial] Initial * [master] Merge branch 'side' - ! [side] Side ---- - - [master] Merge branch 'side' - *+ [side] Side - * [master^] Second -+*+ [initial] Initial + ! [rearrange] Rearranged lines in dir/sub + ! [side] Side +---- + + [rearrange] Rearranged lines in dir/sub + - [master] Merge branch 'side' + * + [side] Side + * [master^] Third + * [master~2] Second ++*++ [initial] Initial EOF V=`git version | sed -e 's/^git version //' -e 's/\./\\./g'` @@ -287,6 +300,8 @@ diff --no-index --name-status -- dir2 dir diff --no-index dir dir3 diff master master^ side diff --dirstat master~1 master~2 +diff --dirstat initial rearrange +diff --dirstat-by-file initial rearrange EOF test_expect_success 'log -S requires an argument' ' diff --git a/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange b/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange new file mode 100644 index 0000000000..e48e33f678 --- /dev/null +++ b/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange @@ -0,0 +1,3 @@ +$ git diff --dirstat-by-file initial rearrange + 100.0% dir/ +$ diff --git a/t/t4013/diff.diff_--dirstat_initial_rearrange b/t/t4013/diff.diff_--dirstat_initial_rearrange new file mode 100644 index 0000000000..5fb02c13bc --- /dev/null +++ b/t/t4013/diff.diff_--dirstat_initial_rearrange @@ -0,0 +1,3 @@ +$ git diff --dirstat initial rearrange + 100.0% dir/ +$ diff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ index 1f0f9ad44b..3b4e113012 100644 --- a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ +++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ @@ -1,7 +1,7 @@ $ git format-patch --stdout --cover-letter -n initial..master^ From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001 From: C O Mitter <committer@example.com> -Date: Mon, 26 Jun 2006 00:05:00 +0000 +Date: Mon, 26 Jun 2006 00:06:00 +0000 Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE *** *** BLURB HERE *** diff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all index d155e0bab2..44d45257da 100644 --- a/t/t4013/diff.log_--decorate=full_--all +++ b/t/t4013/diff.log_--decorate=full_--all @@ -1,4 +1,10 @@ $ git log --decorate=full --all +commit cd4e72fd96faed3f0ba949dc42967430374e2290 (refs/heads/rearrange) +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:06:00 2006 +0000 + + Rearranged lines in dir/sub + commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, refs/heads/master) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all index fd7c3e6439..27d3eabc26 100644 --- a/t/t4013/diff.log_--decorate_--all +++ b/t/t4013/diff.log_--decorate_--all @@ -1,4 +1,10 @@ $ git log --decorate --all +commit cd4e72fd96faed3f0ba949dc42967430374e2290 (rearrange) +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:06:00 2006 +0000 + + Rearranged lines in dir/sub + commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, master) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index c3cdb52053..045cee312c 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -614,13 +614,13 @@ echo "fatal: --name-only does not make sense" > expect.name-only echo "fatal: --name-status does not make sense" > expect.name-status echo "fatal: --check does not make sense" > expect.check -test_expect_success C_LOCALE_OUTPUT 'options no longer allowed for format-patch' ' +test_expect_success 'options no longer allowed for format-patch' ' test_must_fail git format-patch --name-only 2> output && - test_cmp expect.name-only output && + test_i18ncmp expect.name-only output && test_must_fail git format-patch --name-status 2> output && - test_cmp expect.name-status output && + test_i18ncmp expect.name-status output && test_must_fail git format-patch --check 2> output && - test_cmp expect.check output' + test_i18ncmp expect.check output' test_expect_success 'format-patch --numstat should produce a patch' ' git format-patch --numstat --stdout master..side > output && @@ -793,4 +793,62 @@ test_expect_success 'format-patch wraps extremely long headers (rfc2047)' ' test_cmp expect subject ' +M8="foo_bar_" +M64=$M8$M8$M8$M8$M8$M8$M8$M8 +cat >expect <<EOF +From: $M64 + <foobar@foo.bar> +EOF +test_expect_success 'format-patch wraps non-quotable headers' ' + rm -rf patches/ && + echo content >>file && + git add file && + git commit -mfoo --author "$M64 <foobar@foo.bar>" && + git format-patch --stdout -1 >patch && + sed -n "/^From: /p; /^ /p; /^$/q" <patch >from && + test_cmp expect from +' + +check_author() { + echo content >>file && + git add file && + GIT_AUTHOR_NAME=$1 git commit -m author-check && + git format-patch --stdout -1 >patch && + grep ^From: patch >actual && + test_cmp expect actual +} + +cat >expect <<'EOF' +From: "Foo B. Bar" <author@example.com> +EOF +test_expect_success 'format-patch quotes dot in headers' ' + check_author "Foo B. Bar" +' + +cat >expect <<'EOF' +From: "Foo \"The Baz\" Bar" <author@example.com> +EOF +test_expect_success 'format-patch quotes double-quote in headers' ' + check_author "Foo \"The Baz\" Bar" +' + +cat >expect <<'EOF' +From: =?UTF-8?q?"F=C3=B6o=20B.=20Bar"?= <author@example.com> +EOF +test_expect_success 'rfc2047-encoded headers also double-quote 822 specials' ' + check_author "Föo B. Bar" +' + +cat >expect <<'EOF' +Subject: header with . in it +EOF +test_expect_success 'subject lines do not have 822 atom-quoting' ' + echo content >>file && + git add file && + git commit -m "header with . in it" && + git format-patch -k -1 --stdout >patch && + grep ^Subject: patch >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh index 2a537a21e8..c00a94b9ba 100755 --- a/t/t4022-diff-rewrite.sh +++ b/t/t4022-diff-rewrite.sh @@ -11,7 +11,9 @@ test_expect_success setup ' tr \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \ "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \ - <"$TEST_DIRECTORY"/../COPYING >test + <"$TEST_DIRECTORY"/../COPYING >test && + echo "to be deleted" >test2 && + git add test2 ' @@ -25,5 +27,44 @@ test_expect_success 'detect rewrite' ' ' +cat >expect <<EOF +diff --git a/test2 b/test2 +deleted file mode 100644 +index 4202011..0000000 +--- a/test2 ++++ /dev/null +@@ -1 +0,0 @@ +-to be deleted +EOF +test_expect_success 'show deletion diff without -D' ' + + rm test2 && + git diff -- test2 >actual && + test_cmp expect actual +' + +cat >expect <<EOF +diff --git a/test2 b/test2 +deleted file mode 100644 +index 4202011..0000000 +EOF +test_expect_success 'suppress deletion diff with -D' ' + + git diff -D -- test2 >actual && + test_cmp expect actual +' + +test_expect_success 'show deletion diff with -B' ' + + git diff -B -- test >actual && + grep "Linus Torvalds" actual +' + +test_expect_success 'suppress deletion diff with -B -D' ' + + git diff -B -D -- test >actual && + grep -v "Linus Torvalds" actual +' + test_done diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index af78e21ba9..a1fddd4d15 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -66,12 +66,9 @@ test_expect_success "fetch --recurse-submodules recurses into submodules" ' ( cd downstream && git fetch --recurse-submodules >../actual.out 2>../actual.err - ) -' - -test_expect_success C_LOCALE_OUTPUT "fetch --recurse-submodules recurses into submodules: output" ' - test_cmp expect.out actual.out && - test_cmp expect.err actual.err + ) && + test_i18ncmp expect.out actual.out && + test_i18ncmp expect.err actual.err ' test_expect_success "fetch alone only fetches superproject" ' @@ -98,12 +95,9 @@ test_expect_success "using fetchRecurseSubmodules=true in .gitmodules recurses i cd downstream && git config -f .gitmodules submodule.submodule.fetchRecurseSubmodules true && git fetch >../actual.out 2>../actual.err - ) -' - -test_expect_success C_LOCALE_OUTPUT "using fetchRecurseSubmodules=true in .gitmodules recurses into submodules" ' - test_cmp expect.out actual.out && - test_cmp expect.err actual.err + ) && + test_i18ncmp expect.out actual.out && + test_i18ncmp expect.err actual.err ' test_expect_success "--no-recurse-submodules overrides .gitmodules config" ' @@ -132,12 +126,9 @@ test_expect_success "--recurse-submodules overrides fetchRecurseSubmodules setti git fetch --recurse-submodules >../actual.out 2>../actual.err && git config --unset -f .gitmodules submodule.submodule.fetchRecurseSubmodules && git config --unset submodule.submodule.fetchRecurseSubmodules - ) -' - -test_expect_success C_LOCALE_OUTPUT "--recurse-submodules overrides fetchRecurseSubmodules setting from .git/config: output" ' - test_cmp expect.out actual.out && - test_cmp expect.err actual.err + ) && + test_i18ncmp expect.out actual.out && + test_i18ncmp expect.err actual.err ' test_expect_success "--quiet propagates to submodules" ' @@ -154,24 +145,18 @@ test_expect_success "--dry-run propagates to submodules" ' ( cd downstream && git fetch --recurse-submodules --dry-run >../actual.out 2>../actual.err - ) -' - -test_expect_success C_LOCALE_OUTPUT "--dry-run propagates to submodules: output" ' - test_cmp expect.out actual.out && - test_cmp expect.err actual.err + ) && + test_i18ncmp expect.out actual.out && + test_i18ncmp expect.err actual.err ' test_expect_success "Without --dry-run propagates to submodules" ' ( cd downstream && git fetch --recurse-submodules >../actual.out 2>../actual.err - ) -' - -test_expect_success C_LOCALE_OUTPUT "Without --dry-run propagates to submodules: output" ' - test_cmp expect.out actual.out && - test_cmp expect.err actual.err + ) && + test_i18ncmp expect.out actual.out && + test_i18ncmp expect.err actual.err ' test_expect_success "recurseSubmodules=true propagates into submodules" ' @@ -180,12 +165,9 @@ test_expect_success "recurseSubmodules=true propagates into submodules" ' cd downstream && git config fetch.recurseSubmodules true git fetch >../actual.out 2>../actual.err - ) -' - -test_expect_success C_LOCALE_OUTPUT "recurseSubmodules=true propagates into submodules: output" ' - test_cmp expect.out actual.out && - test_cmp expect.err actual.err + ) && + test_i18ncmp expect.out actual.out && + test_i18ncmp expect.err actual.err ' test_expect_success "--recurse-submodules overrides config in submodule" ' @@ -197,12 +179,9 @@ test_expect_success "--recurse-submodules overrides config in submodule" ' git config fetch.recurseSubmodules false ) && git fetch --recurse-submodules >../actual.out 2>../actual.err - ) -' - -test_expect_success C_LOCALE_OUTPUT "--recurse-submodules overrides config in submodule: output" ' - test_cmp expect.out actual.out && - test_cmp expect.err actual.err + ) && + test_i18ncmp expect.out actual.out && + test_i18ncmp expect.err actual.err ' test_expect_success "--no-recurse-submodules overrides config setting" ' @@ -243,8 +222,8 @@ test_expect_success "Recursion stops when no new submodule commits are fetched" cd downstream && git fetch >../actual.out 2>../actual.err ) && - test_cmp expect.err.sub actual.err && - test_cmp expect.out.sub actual.out + test_i18ncmp expect.err.sub actual.err && + test_i18ncmp expect.out.sub actual.out ' test_expect_success "Recursion doesn't happen when new superproject commits don't change any submodules" ' @@ -261,7 +240,7 @@ test_expect_success "Recursion doesn't happen when new superproject commits don' git fetch >../actual.out 2>../actual.err ) && ! test -s actual.out && - test_cmp expect.err.file actual.err + test_i18ncmp expect.err.file actual.err ' test_expect_success "Recursion picks up config in submodule" ' @@ -289,8 +268,8 @@ test_expect_success "Recursion picks up config in submodule" ' git config --unset fetch.recurseSubmodules ) ) && - test_cmp expect.err.sub actual.err && - test_cmp expect.out actual.out + test_i18ncmp expect.err.sub actual.err && + test_i18ncmp expect.out actual.out ' test_expect_success "Recursion picks up all submodules when necessary" ' @@ -321,8 +300,8 @@ test_expect_success "Recursion picks up all submodules when necessary" ' cd downstream && git fetch >../actual.out 2>../actual.err ) && - test_cmp expect.err.2 actual.err && - test_cmp expect.out actual.out + test_i18ncmp expect.err.2 actual.err && + test_i18ncmp expect.out actual.out ' test_expect_success "'--recurse-submodules=on-demand' doesn't recurse when no new commits are fetched in the superproject (and ignores config)" ' @@ -375,8 +354,8 @@ test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necess git config --unset -f .gitmodules submodule.deepsubmodule.fetchRecursive ) ) && - test_cmp expect.out actual.out && - test_cmp expect.err actual.err + test_i18ncmp expect.out actual.out && + 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)" ' @@ -393,7 +372,7 @@ test_expect_success "'--recurse-submodules=on-demand' stops when no new submodul git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err ) && ! test -s actual.out && - test_cmp expect.err.file actual.err + test_i18ncmp expect.err.file actual.err ' test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config" ' @@ -420,8 +399,8 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config cd downstream && git config --unset fetch.recurseSubmodules ) && - test_cmp expect.out.sub actual.out && - test_cmp expect.err.2 actual.err + test_i18ncmp expect.out.sub actual.out && + test_i18ncmp expect.err.2 actual.err ' test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' overrides fetch.recurseSubmodules" ' @@ -448,8 +427,8 @@ test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' override cd downstream && git config --unset submodule.submodule.fetchRecurseSubmodules ) && - test_cmp expect.out.sub actual.out && - test_cmp expect.err.2 actual.err + test_i18ncmp expect.out.sub actual.out && + test_i18ncmp expect.err.2 actual.err ' test_expect_success "don't fetch submodule when newly recorded commits are already present" ' @@ -468,7 +447,7 @@ test_expect_success "don't fetch submodule when newly recorded commits are alrea git fetch >../actual.out 2>../actual.err ) && ! test -s actual.out && - test_cmp expect.err actual.err + test_i18ncmp expect.err actual.err ' test_done diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh index 0492877d51..d924056c8a 100755 --- a/t/t5541-http-push.sh +++ b/t/t5541-http-push.sh @@ -135,8 +135,8 @@ test_expect_success 'push fails for non-fast-forward refs unmatched by remote he grep "^ ! \[rejected\] *master -> retsam (non-fast-forward)$" output ' -test_expect_success C_LOCALE_OUTPUT 'push fails for non-fast-forward refs unmatched by remote helper: our output' ' - grep "To prevent you from losing history, non-fast-forward updates were rejected" \ +test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' ' + test_i18ngrep "To prevent you from losing history, non-fast-forward updates were rejected" \ output ' diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 5a068b21e4..151ea531bd 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -194,11 +194,14 @@ test_expect_success 'do not respect url-encoding of non-url path' ' test_expect_success 'clone separate gitdir' ' rm -rf dst && git clone --separate-git-dir realgitdir src dst && - echo "gitdir: `pwd`/realgitdir" >expected && - test_cmp expected dst/.git && test -d realgitdir/refs ' +test_expect_success 'clone separate gitdir: output' ' + echo "gitdir: `pwd`/realgitdir" >expected && + test_cmp expected dst/.git +' + test_expect_success 'clone separate gitdir where target already exists' ' rm -rf dst && test_must_fail git clone --separate-git-dir realgitdir src dst diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh index cacf3de6c9..28d4f6b259 100755 --- a/t/t6007-rev-list-cherry-pick-file.sh +++ b/t/t6007-rev-list-cherry-pick-file.sh @@ -157,6 +157,33 @@ test_expect_success '--cherry' ' test_cmp actual.named expect ' +cat >expect <<EOF +1 1 +EOF + +test_expect_success '--cherry --count' ' + git rev-list --cherry --count F...E -- bar > actual && + test_cmp actual expect +' + +cat >expect <<EOF +2 2 +EOF + +test_expect_success '--cherry-mark --count' ' + git rev-list --cherry-mark --count F...E -- bar > actual && + test_cmp actual expect +' + +cat >expect <<EOF +1 1 2 +EOF + +test_expect_success '--cherry-mark --left-right --count' ' + git rev-list --cherry-mark --left-right --count F...E -- bar > actual && + test_cmp actual expect +' + test_expect_success '--cherry-pick with independent, but identical branches' ' git symbolic-ref HEAD refs/heads/independent && rm .git/index && diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh index 1ed259d864..5f3b604fd9 100755 --- a/t/t6022-merge-rename.sh +++ b/t/t6022-merge-rename.sh @@ -609,4 +609,67 @@ test_expect_success 'check handling of differently renamed file with D/F conflic ! test -f original ' +test_expect_success 'setup avoid unnecessary update, normal rename' ' + git reset --hard && + git checkout --orphan avoid-unnecessary-update-1 && + git rm -rf . && + git clean -fdqx && + + printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >original && + git add -A && + git commit -m "Common commmit" && + + git mv original rename && + echo 11 >>rename && + git add -u && + git commit -m "Renamed and modified" && + + git checkout -b merge-branch-1 HEAD~1 && + echo "random content" >random-file && + git add -A && + git commit -m "Random, unrelated changes" +' + +test_expect_success 'avoid unnecessary update, normal rename' ' + git checkout -q avoid-unnecessary-update-1^0 && + test-chmtime =1000000000 rename && + test-chmtime -v +0 rename >expect && + git merge merge-branch-1 && + test-chmtime -v +0 rename >actual && + test_cmp expect actual # "rename" should have stayed intact +' + +test_expect_success 'setup to test avoiding unnecessary update, with D/F conflict' ' + git reset --hard && + git checkout --orphan avoid-unnecessary-update-2 && + git rm -rf . && + git clean -fdqx && + + mkdir df && + printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >df/file && + git add -A && + git commit -m "Common commmit" && + + git mv df/file temp && + rm -rf df && + git mv temp df && + echo 11 >>df && + git add -u && + git commit -m "Renamed and modified" && + + git checkout -b merge-branch-2 HEAD~1 && + >unrelated-change && + git add unrelated-change && + git commit -m "Only unrelated changes" +' + +test_expect_failure 'avoid unnecessary update, with D/F conflict' ' + git checkout -q avoid-unnecessary-update-2^0 && + test-chmtime =1000000000 df && + test-chmtime -v +0 df >expect && + git merge merge-branch-2 && + test-chmtime -v +0 df >actual && + test_cmp expect actual # "df" should have stayed intact +' + test_done diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh index 6c3719141a..a9b0ac1efc 100755 --- a/t/t6040-tracking-info.sh +++ b/t/t6040-tracking-info.sh @@ -42,13 +42,13 @@ b3 behind 1 b4 ahead 2 EOF -test_expect_success C_LOCALE_OUTPUT 'branch -v' ' +test_expect_success 'branch -v' ' ( cd test && git branch -v ) | sed -n -e "$script" >actual && - test_cmp expect actual + test_i18ncmp expect actual ' test_expect_success 'checkout' ' diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index 1826996245..f67aa6ff6a 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -123,8 +123,8 @@ cat - >err.expect <<EOF warning: tag 'A' is really 'Q' here EOF check_describe A-* HEAD -test_expect_success C_LOCALE_OUTPUT 'warning was displayed for Q' ' - test_cmp err.expect err.actual +test_expect_success 'warning was displayed for Q' ' + test_i18ncmp err.expect err.actual ' test_expect_success 'rename tag Q back to A' ' mv .git/refs/tags/Q .git/refs/tags/A diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 1dedfd0c83..2ac1c66079 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -1120,13 +1120,11 @@ test_expect_success \ ! (GIT_EDITOR=cat git tag -a initial-comment > actual) ' -test_expect_success \ - C_LOCALE_OUTPUT \ - 'message in editor has initial comment: first line' ' +test_expect_success 'message in editor has initial comment: first line' ' # check the first line --- should be empty echo >first.expect && sed -e 1q <actual >first.actual && - test_cmp first.expect first.actual + test_i18ncmp first.expect first.actual ' test_expect_success \ diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh index 14fcb1c7f5..cffb2696d4 100755 --- a/t/t7012-skip-worktree-writing.sh +++ b/t/t7012-skip-worktree-writing.sh @@ -124,16 +124,16 @@ cat >expected <<EOF Would remove expected Would remove result EOF -test_expect_success C_LOCALE_OUTPUT 'git-clean, absent case' ' +test_expect_success 'git-clean, absent case' ' setup_absent && git clean -n > result && - test_cmp expected result + test_i18ncmp expected result ' -test_expect_success C_LOCALE_OUTPUT 'git-clean, dirty case' ' +test_expect_success 'git-clean, dirty case' ' setup_dirty && git clean -n > result && - test_cmp expected result + test_i18ncmp expected result ' #TODO test_expect_failure 'git-apply adds file' false diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh index b4fcc86a10..b8cb4906aa 100755 --- a/t/t7060-wtstatus.sh +++ b/t/t7060-wtstatus.sh @@ -38,7 +38,7 @@ cat >expect <<EOF no changes added to commit (use "git add" and/or "git commit -a") EOF -test_expect_success C_LOCALE_OUTPUT 'M/D conflict does not segfault' ' +test_expect_success 'M/D conflict does not segfault' ' mkdir mdconflict && ( cd mdconflict && @@ -50,10 +50,72 @@ test_expect_success C_LOCALE_OUTPUT 'M/D conflict does not segfault' ' git commit -m delete && test_must_fail git merge master && test_must_fail git commit --dry-run >../actual && - test_cmp ../expect ../actual && + test_i18ncmp ../expect ../actual && git status >../actual && - test_cmp ../expect ../actual + test_i18ncmp ../expect ../actual ) ' +test_expect_success 'rename & unmerged setup' ' + git rm -f -r . && + cat "$TEST_DIRECTORY/README" >ONE && + git add ONE && + test_tick && + git commit -m "One commit with ONE" && + + echo Modified >TWO && + cat ONE >>TWO && + cat ONE >>THREE && + git add TWO THREE && + sha1=$(git rev-parse :ONE) && + git rm --cached ONE && + ( + echo "100644 $sha1 1 ONE" && + echo "100644 $sha1 2 ONE" && + echo "100644 $sha1 3 ONE" + ) | git update-index --index-info && + echo Further >>THREE +' + +test_expect_success 'rename & unmerged status' ' + git status -suno >actual && + cat >expect <<-EOF && + UU ONE + AM THREE + A TWO + EOF + test_cmp expect actual +' + +test_expect_success 'git diff-index --cached shows 2 added + 1 unmerged' ' + cat >expected <<-EOF && + U ONE + A THREE + A TWO + EOF + git diff-index --cached --name-status HEAD >actual && + test_cmp expected actual +' + +test_expect_success 'git diff-index --cached -M shows 2 added + 1 unmerged' ' + cat >expected <<-EOF && + U ONE + A THREE + A TWO + EOF + git diff-index --cached --name-status HEAD >actual && + test_cmp expected actual +' + +test_expect_success 'git diff-index --cached -C shows 2 copies + 1 unmerged' ' + cat >expected <<-EOF && + U ONE + C ONE THREE + C ONE TWO + EOF + git diff-index --cached -C --name-status HEAD | + sed "s/^C[0-9]*/C/g" >actual && + test_cmp expected actual +' + test_done diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index 7be2ff38fc..f1cfc9ac95 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -423,10 +423,10 @@ Unstaged changes after reset: M file2 EOF -test_expect_success C_LOCALE_OUTPUT '--mixed refreshes the index' ' +test_expect_success '--mixed refreshes the index' ' echo 123 >> file2 && git reset --mixed HEAD > output && - test_cmp expect output + test_i18ncmp expect output ' test_expect_success 'disambiguation (1)' ' diff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh index b42820ad69..a82a07a04a 100755 --- a/t/t7110-reset-merge.sh +++ b/t/t7110-reset-merge.sh @@ -233,11 +233,11 @@ test_expect_success '"reset --merge HEAD^" is ok with pending merge' ' # working index HEAD target working index HEAD # ---------------------------------------------------- # file1: X U B C --keep (disallowed) -test_expect_success C_LOCALE_OUTPUT '"reset --keep HEAD^" fails with pending merge' ' +test_expect_success '"reset --keep HEAD^" fails with pending merge' ' git reset --hard third && test_must_fail git merge branch1 && test_must_fail git reset --keep HEAD^ 2>err.log && - grep "middle of a merge" err.log + test_i18ngrep "middle of a merge" err.log ' # The next test will test the following: @@ -259,11 +259,11 @@ test_expect_success '"reset --merge HEAD" is ok with pending merge' ' # working index HEAD target working index HEAD # ---------------------------------------------------- # file1: X U B B --keep (disallowed) -test_expect_success C_LOCALE_OUTPUT '"reset --keep HEAD" fails with pending merge' ' +test_expect_success '"reset --keep HEAD" fails with pending merge' ' git reset --hard third && test_must_fail git merge branch1 && test_must_fail git reset --keep HEAD 2>err.log && - grep "middle of a merge" err.log + test_i18ngrep "middle of a merge" err.log ' test_expect_success '--merge is ok with added/deleted merge' ' @@ -280,7 +280,7 @@ test_expect_success '--merge is ok with added/deleted merge' ' git diff --exit-code --cached ' -test_expect_success C_LOCALE_OUTPUT '--keep fails with added/deleted merge' ' +test_expect_success '--keep fails with added/deleted merge' ' git reset --hard third && rm -f file2 && test_must_fail git merge branch3 && @@ -289,7 +289,7 @@ test_expect_success C_LOCALE_OUTPUT '--keep fails with added/deleted merge' ' git diff --exit-code file3 && git diff --exit-code branch3 file3 && test_must_fail git reset --keep HEAD 2>err.log && - grep "middle of a merge" err.log + test_i18ngrep "middle of a merge" err.log ' test_done diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 37ed0931d9..07fb53adcb 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -223,12 +223,12 @@ test_expect_success 'checkout --merge --conflict=diff3 <branch>' ' test_cmp two expect ' -test_expect_success C_LOCALE_OUTPUT 'checkout to detach HEAD (with advice declined)' ' +test_expect_success 'checkout to detach HEAD (with advice declined)' ' git config advice.detachedHead false && git checkout -f renamer && git clean -f && git checkout renamer^ 2>messages && - grep "HEAD is now at 7329388" messages && + test_i18ngrep "HEAD is now at 7329388" messages && test 1 -eq $(wc -l <messages) && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/master) && @@ -242,11 +242,11 @@ test_expect_success C_LOCALE_OUTPUT 'checkout to detach HEAD (with advice declin fi ' -test_expect_success C_LOCALE_OUTPUT 'checkout to detach HEAD' ' +test_expect_success 'checkout to detach HEAD' ' git config advice.detachedHead true && git checkout -f renamer && git clean -f && git checkout renamer^ 2>messages && - grep "HEAD is now at 7329388" messages && + test_i18ngrep "HEAD is now at 7329388" messages && test 1 -lt $(wc -l <messages) && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/master) && @@ -260,7 +260,7 @@ test_expect_success C_LOCALE_OUTPUT 'checkout to detach HEAD' ' fi ' -test_expect_success C_LOCALE_OUTPUT 'checkout to detach HEAD with branchname^' ' +test_expect_success 'checkout to detach HEAD with branchname^' ' git checkout -f master && git clean -f && git checkout renamer^ && @@ -276,7 +276,7 @@ test_expect_success C_LOCALE_OUTPUT 'checkout to detach HEAD with branchname^' ' fi ' -test_expect_success C_LOCALE_OUTPUT 'checkout to detach HEAD with :/message' ' +test_expect_success 'checkout to detach HEAD with :/message' ' git checkout -f master && git clean -f && git checkout ":/Initial" && @@ -292,7 +292,7 @@ test_expect_success C_LOCALE_OUTPUT 'checkout to detach HEAD with :/message' ' fi ' -test_expect_success C_LOCALE_OUTPUT 'checkout to detach HEAD with HEAD^0' ' +test_expect_success 'checkout to detach HEAD with HEAD^0' ' git checkout -f master && git clean -f && git checkout HEAD^0 && diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index bf7c788735..4f16fcce2b 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -94,6 +94,29 @@ test_expect_success 'submodule update does not fetch already present commits' ' ! test -s actual.err ' +test_expect_success 'submodule update should fail due to local changes' ' + (cd super/submodule && + git reset --hard HEAD~1 && + echo "local change" > file + ) && + (cd super && + (cd submodule && + compare_head + ) && + test_must_fail git submodule update submodule + ) +' +test_expect_success 'submodule update should throw away changes with --force ' ' + (cd super && + (cd submodule && + compare_head + ) && + git submodule update --force submodule && + cd submodule && + ! compare_head + ) +' + test_expect_success 'submodule update --rebase staying on master' ' (cd super/submodule && git checkout master diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index bcdf0847d0..47096f9014 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -15,7 +15,7 @@ commit_msg_is () { printf "%s" "$(git log --pretty=format:%s%b -1)" >$expect && printf "%s" "$1" >$actual && - test_cmp $expect $actual + test_i18ncmp $expect $actual } # A sanity check to see if commit is working at all. @@ -72,7 +72,7 @@ test_expect_success 'adding comments to a template should not commit' ' ) ' -test_expect_success C_LOCALE_OUTPUT 'adding real content to a template should commit' ' +test_expect_success 'adding real content to a template should commit' ' ( test_set_editor "$TEST_DIRECTORY"/t7500/add-content && git commit --template "$TEMPLATE" @@ -80,7 +80,7 @@ test_expect_success C_LOCALE_OUTPUT 'adding real content to a template should co commit_msg_is "template linecommit message" ' -test_expect_success C_LOCALE_OUTPUT '-t option should be short for --template' ' +test_expect_success '-t option should be short for --template' ' echo "short template" > "$TEMPLATE" && echo "new content" >> foo && git add foo && @@ -91,7 +91,7 @@ test_expect_success C_LOCALE_OUTPUT '-t option should be short for --template' ' commit_msg_is "short templatecommit message" ' -test_expect_success C_LOCALE_OUTPUT 'config-specified template should commit' ' +test_expect_success 'config-specified template should commit' ' echo "new template" > "$TEMPLATE" && git config commit.template "$TEMPLATE" && echo "more content" >> foo && @@ -290,7 +290,7 @@ test_expect_success 'commit --squash works with -c for same commit' ' commit_msg_is "squash! edited commit" ' -test_expect_success C_LOCALE_OUTPUT 'commit --squash works with editor' ' +test_expect_success 'commit --squash works with editor' ' commit_for_rebase_autosquash_setup && test_set_editor "$TEST_DIRECTORY"/t7500/add-content && git commit --squash HEAD~1 && diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index a76c474195..7f7f7c7b95 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -16,9 +16,10 @@ test_expect_success \ "echo 'bongo bongo' >file && git add file" -test_expect_success C_LOCALE_OUTPUT \ - "Constructing initial commit" \ - "git status | grep 'Initial commit'" +test_expect_success "Constructing initial commit" ' + git status >actual && + test_i18ngrep "Initial commit" actual +' test_expect_success \ "fail initial amend" \ diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index cfb569eaba..3f3adc31b9 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -22,10 +22,7 @@ check_summary_oneline() { SUMMARY_POSTFIX="$(git log -1 --pretty='format:%h')" echo "[$SUMMARY_PREFIX $SUMMARY_POSTFIX] $2" >exp && - if test_have_prereq C_LOCALE_OUTPUT - then - test_cmp exp act - fi + test_i18ncmp exp act } test_expect_success 'output summary format' ' @@ -234,23 +231,19 @@ echo "sample # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit." >expect -test_expect_success C_LOCALE_OUTPUT 'cleanup commit messages (strip,-F,-e): output' ' - test_cmp expect actual +test_expect_success 'cleanup commit messages (strip,-F,-e): output' ' + test_i18ncmp expect actual ' echo "# # Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> #" >> expect -test_expect_success C_LOCALE_OUTPUT 'author different from committer' ' - +test_expect_success 'author different from committer' ' echo >>negative && - git commit -e -m "sample" - head -n 7 .git/COMMIT_EDITMSG >actual -' - -test_expect_success C_LOCALE_OUTPUT 'author different from committer: output' ' - test_cmp expect actual + test_might_fail git commit -e -m "sample" && + head -n 7 .git/COMMIT_EDITMSG >actual && + test_i18ncmp expect actual ' mv expect expect.tmp @@ -259,7 +252,7 @@ rm -f expect.tmp echo "# Committer: #" >> expect -test_expect_success C_LOCALE_OUTPUT 'committer is automatic' ' +test_expect_success 'committer is automatic' ' echo >>negative && ( @@ -270,10 +263,7 @@ test_expect_success C_LOCALE_OUTPUT 'committer is automatic' ' ) && head -n 8 .git/COMMIT_EDITMSG | \ sed "s/^# Committer: .*/# Committer:/" >actual -' - -test_expect_success C_LOCALE_OUTPUT 'committer is automatic: output' ' - test_cmp expect actual + test_i18ncmp expect actual ' pwd=`pwd` @@ -376,78 +366,78 @@ try_commit () { GIT_EDITOR=.git/FAKE_EDITOR git commit -a $* $use_template && case "$use_template" in '') - ! grep "^## Custom template" .git/COMMIT_EDITMSG ;; + test_i18ngrep ! "^## Custom template" .git/COMMIT_EDITMSG ;; *) - grep "^## Custom template" .git/COMMIT_EDITMSG ;; + test_i18ngrep "^## Custom template" .git/COMMIT_EDITMSG ;; esac } try_commit_status_combo () { - test_expect_success C_LOCALE_OUTPUT 'commit' ' + test_expect_success 'commit' ' clear_config commit.status && try_commit "" && - grep "^# Changes to be committed:" .git/COMMIT_EDITMSG + test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG ' - test_expect_success C_LOCALE_OUTPUT 'commit' ' + test_expect_success 'commit' ' clear_config commit.status && try_commit "" && - grep "^# Changes to be committed:" .git/COMMIT_EDITMSG + test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG ' - test_expect_success C_LOCALE_OUTPUT 'commit --status' ' + test_expect_success 'commit --status' ' clear_config commit.status && try_commit --status && - grep "^# Changes to be committed:" .git/COMMIT_EDITMSG + test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG ' - test_expect_success C_LOCALE_OUTPUT 'commit --no-status' ' + test_expect_success 'commit --no-status' ' clear_config commit.status && try_commit --no-status && - ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG + test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG ' - test_expect_success C_LOCALE_OUTPUT 'commit with commit.status = yes' ' + test_expect_success 'commit with commit.status = yes' ' clear_config commit.status && git config commit.status yes && try_commit "" && - grep "^# Changes to be committed:" .git/COMMIT_EDITMSG + test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG ' - test_expect_success C_LOCALE_OUTPUT 'commit with commit.status = no' ' + test_expect_success 'commit with commit.status = no' ' clear_config commit.status && git config commit.status no && try_commit "" && - ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG + test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG ' - test_expect_success C_LOCALE_OUTPUT 'commit --status with commit.status = yes' ' + test_expect_success 'commit --status with commit.status = yes' ' clear_config commit.status && git config commit.status yes && try_commit --status && - grep "^# Changes to be committed:" .git/COMMIT_EDITMSG + test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG ' - test_expect_success C_LOCALE_OUTPUT 'commit --no-status with commit.status = yes' ' + test_expect_success 'commit --no-status with commit.status = yes' ' clear_config commit.status && git config commit.status yes && try_commit --no-status && - ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG + test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG ' - test_expect_success C_LOCALE_OUTPUT 'commit --status with commit.status = no' ' + test_expect_success 'commit --status with commit.status = no' ' clear_config commit.status && git config commit.status no && try_commit --status && - grep "^# Changes to be committed:" .git/COMMIT_EDITMSG + test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG ' - test_expect_success C_LOCALE_OUTPUT 'commit --no-status with commit.status = no' ' + test_expect_success 'commit --no-status with commit.status = no' ' clear_config commit.status && git config commit.status no && try_commit --no-status && - ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG + test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG ' } diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh index c56733253f..c8d50a6567 100755 --- a/t/t7506-status-submodule.sh +++ b/t/t7506-status-submodule.sh @@ -20,21 +20,21 @@ test_expect_success 'setup' ' git commit -m "Add submodule sub" ' -test_expect_success C_LOCALE_OUTPUT 'status clean' ' +test_expect_success 'status clean' ' git status >output && - grep "nothing to commit" output + test_i18ngrep "nothing to commit" output ' -test_expect_success C_LOCALE_OUTPUT 'commit --dry-run -a clean' ' +test_expect_success 'commit --dry-run -a clean' ' test_must_fail git commit --dry-run -a >output && - grep "nothing to commit" output + test_i18ngrep "nothing to commit" output ' -test_expect_success C_LOCALE_OUTPUT 'status with modified file in submodule' ' +test_expect_success 'status with modified file in submodule' ' (cd sub && git reset --hard) && echo "changed" >sub/foo && git status >output && - grep "modified: sub (modified content)" output + test_i18ngrep "modified: sub (modified content)" output ' test_expect_success 'status with modified file in submodule (porcelain)' ' @@ -46,10 +46,10 @@ test_expect_success 'status with modified file in submodule (porcelain)' ' EOF ' -test_expect_success C_LOCALE_OUTPUT 'status with added file in submodule' ' +test_expect_success 'status with added file in submodule' ' (cd sub && git reset --hard && echo >foo && git add foo) && git status >output && - grep "modified: sub (modified content)" output + test_i18ngrep "modified: sub (modified content)" output ' test_expect_success 'status with added file in submodule (porcelain)' ' @@ -60,16 +60,16 @@ test_expect_success 'status with added file in submodule (porcelain)' ' EOF ' -test_expect_success C_LOCALE_OUTPUT 'status with untracked file in submodule' ' +test_expect_success 'status with untracked file in submodule' ' (cd sub && git reset --hard) && echo "content" >sub/new-file && git status >output && - grep "modified: sub (untracked content)" output + test_i18ngrep "modified: sub (untracked content)" output ' -test_expect_success C_LOCALE_OUTPUT 'status -uno with untracked file in submodule' ' +test_expect_success 'status -uno with untracked file in submodule' ' git status -uno >output && - grep "^nothing to commit" output + test_i18ngrep "^nothing to commit" output ' test_expect_success 'status with untracked file in submodule (porcelain)' ' @@ -79,11 +79,11 @@ test_expect_success 'status with untracked file in submodule (porcelain)' ' EOF ' -test_expect_success C_LOCALE_OUTPUT 'status with added and untracked file in submodule' ' +test_expect_success 'status with added and untracked file in submodule' ' (cd sub && git reset --hard && echo >foo && git add foo) && echo "content" >sub/new-file && git status >output && - grep "modified: sub (modified content, untracked content)" output + test_i18ngrep "modified: sub (modified content, untracked content)" output ' test_expect_success 'status with added and untracked file in submodule (porcelain)' ' @@ -95,13 +95,13 @@ test_expect_success 'status with added and untracked file in submodule (porcelai EOF ' -test_expect_success C_LOCALE_OUTPUT 'status with modified file in modified submodule' ' +test_expect_success 'status with modified file in modified submodule' ' (cd sub && git reset --hard) && rm sub/new-file && (cd sub && echo "next change" >foo && git commit -m "next change" foo) && echo "changed" >sub/foo && git status >output && - grep "modified: sub (new commits, modified content)" output + test_i18ngrep "modified: sub (new commits, modified content)" output ' test_expect_success 'status with modified file in modified submodule (porcelain)' ' @@ -113,10 +113,10 @@ test_expect_success 'status with modified file in modified submodule (porcelain) EOF ' -test_expect_success C_LOCALE_OUTPUT 'status with added file in modified submodule' ' +test_expect_success 'status with added file in modified submodule' ' (cd sub && git reset --hard && echo >foo && git add foo) && git status >output && - grep "modified: sub (new commits, modified content)" output + test_i18ngrep "modified: sub (new commits, modified content)" output ' test_expect_success 'status with added file in modified submodule (porcelain)' ' @@ -127,11 +127,11 @@ test_expect_success 'status with added file in modified submodule (porcelain)' ' EOF ' -test_expect_success C_LOCALE_OUTPUT 'status with untracked file in modified submodule' ' +test_expect_success 'status with untracked file in modified submodule' ' (cd sub && git reset --hard) && echo "content" >sub/new-file && git status >output && - grep "modified: sub (new commits, untracked content)" output + test_i18ngrep "modified: sub (new commits, untracked content)" output ' test_expect_success 'status with untracked file in modified submodule (porcelain)' ' @@ -141,11 +141,11 @@ test_expect_success 'status with untracked file in modified submodule (porcelain EOF ' -test_expect_success C_LOCALE_OUTPUT 'status with added and untracked file in modified submodule' ' +test_expect_success 'status with added and untracked file in modified submodule' ' (cd sub && git reset --hard && echo >foo && git add foo) && echo "content" >sub/new-file && git status >output && - grep "modified: sub (new commits, modified content, untracked content)" output + test_i18ngrep "modified: sub (new commits, modified content, untracked content)" output ' test_expect_success 'status with added and untracked file in modified submodule (porcelain)' ' @@ -167,24 +167,24 @@ test_expect_success 'setup .git file for sub' ' git commit -m "added .real to .gitignore" .gitignore ' -test_expect_success C_LOCALE_OUTPUT 'status with added file in modified submodule with .git file' ' +test_expect_success 'status with added file in modified submodule with .git file' ' (cd sub && git reset --hard && echo >foo && git add foo) && git status >output && - grep "modified: sub (new commits, modified content)" output + test_i18ngrep "modified: sub (new commits, modified content)" output ' test_expect_success 'rm submodule contents' ' rm -rf sub/* sub/.git ' -test_expect_success C_LOCALE_OUTPUT 'status clean (empty submodule dir)' ' +test_expect_success 'status clean (empty submodule dir)' ' git status >output && - grep "nothing to commit" output + test_i18ngrep "nothing to commit" output ' -test_expect_success C_LOCALE_OUTPUT 'status -a clean (empty submodule dir)' ' +test_expect_success 'status -a clean (empty submodule dir)' ' test_must_fail git commit --dry-run -a >output && - grep "nothing to commit" output + test_i18ngrep "nothing to commit" output ' test_done diff --git a/t/t7508-status.sh b/t/t7508-status.sh index a93e70fac4..cd6e2c5e87 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -16,7 +16,7 @@ test_expect_success 'status -h in broken repository' ' echo "[status] showuntrackedfiles = CORRUPT" >>.git/config && test_expect_code 129 git status -h >usage 2>&1 ) && - grep "[Uu]sage" broken/usage + test_i18ngrep "[Uu]sage" broken/usage ' test_expect_success 'commit -h in broken repository' ' @@ -28,7 +28,7 @@ test_expect_success 'commit -h in broken repository' ' echo "[status] showuntrackedfiles = CORRUPT" >>.git/config && test_expect_code 129 git commit -h >usage 2>&1 ) && - grep "[Uu]sage" broken/usage + test_i18ngrep "[Uu]sage" broken/usage ' test_expect_success 'setup' ' @@ -55,10 +55,8 @@ test_expect_success 'setup' ' git add dir2/added ' -test_expect_success C_LOCALE_OUTPUT 'status (1)' ' - - grep "use \"git rm --cached <file>\.\.\.\" to unstage" output - +test_expect_success 'status (1)' ' + test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output ' cat >expect <<\EOF @@ -85,11 +83,9 @@ cat >expect <<\EOF # untracked EOF -test_expect_success C_LOCALE_OUTPUT 'status (2)' ' - +test_expect_success 'status (2)' ' git status >output && - test_cmp expect output - + test_i18ncmp expect output ' cat >expect <<\EOF @@ -109,17 +105,14 @@ cat >expect <<\EOF # untracked EOF -git config advice.statusHints false - -test_expect_success C_LOCALE_OUTPUT 'status (advice.statusHints false)' ' - +test_expect_success 'status (advice.statusHints false)' ' + test_when_finished "git config --unset advice.statusHints" && + git config advice.statusHints false && git status >output && - test_cmp expect output + test_i18ncmp expect output ' -git config --unset advice.statusHints - cat >expect <<\EOF M dir1/modified A dir2/added @@ -178,16 +171,16 @@ cat >expect <<EOF # # Untracked files not listed (use -u option to show untracked files) EOF -test_expect_success C_LOCALE_OUTPUT 'status -uno' ' +test_expect_success 'status -uno' ' git status -uno >output && - test_cmp expect output + test_i18ncmp expect output ' -test_expect_success C_LOCALE_OUTPUT 'status (status.showUntrackedFiles no)' ' +test_expect_success 'status (status.showUntrackedFiles no)' ' git config status.showuntrackedfiles no test_when_finished "git config --unset status.showuntrackedfiles" && git status >output && - test_cmp expect output + test_i18ncmp expect output ' cat >expect <<EOF @@ -201,9 +194,9 @@ cat >expect <<EOF # Untracked files not listed EOF git config advice.statusHints false -test_expect_success C_LOCALE_OUTPUT 'status -uno (advice.statusHints false)' ' +test_expect_success 'status -uno (advice.statusHints false)' ' git status -uno >output && - test_cmp expect output + test_i18ncmp expect output ' git config --unset advice.statusHints @@ -246,16 +239,16 @@ cat >expect <<EOF # output # untracked EOF -test_expect_success C_LOCALE_OUTPUT 'status -unormal' ' +test_expect_success 'status -unormal' ' git status -unormal >output && - test_cmp expect output + test_i18ncmp expect output ' -test_expect_success C_LOCALE_OUTPUT 'status (status.showUntrackedFiles normal)' ' +test_expect_success 'status (status.showUntrackedFiles normal)' ' git config status.showuntrackedfiles normal test_when_finished "git config --unset status.showuntrackedfiles" && git status >output && - test_cmp expect output + test_i18ncmp expect output ' cat >expect <<EOF @@ -305,15 +298,16 @@ cat >expect <<EOF # output # untracked EOF -test_expect_success C_LOCALE_OUTPUT 'status -uall' ' +test_expect_success 'status -uall' ' git status -uall >output && - test_cmp expect output + test_i18ncmp expect output ' -test_expect_success C_LOCALE_OUTPUT 'status (status.showUntrackedFiles all)' ' + +test_expect_success 'status (status.showUntrackedFiles all)' ' git config status.showuntrackedfiles all test_when_finished "git config --unset status.showuntrackedfiles" && git status >output && - test_cmp expect output + test_i18ncmp expect output ' test_expect_success 'teardown dir3' ' @@ -367,11 +361,9 @@ cat >expect <<\EOF # ../untracked EOF -test_expect_success C_LOCALE_OUTPUT 'status with relative paths' ' - +test_expect_success 'status with relative paths' ' (cd dir1 && git status) >output && - test_cmp expect output - + test_i18ncmp expect output ' cat >expect <<\EOF @@ -440,22 +432,18 @@ cat >expect <<\EOF # <BLUE>untracked<RESET> EOF -test_expect_success C_LOCALE_OUTPUT 'status with color.ui' ' - +test_expect_success 'status with color.ui' ' git config color.ui always && test_when_finished "git config --unset color.ui" && git status | test_decode_color >output && - test_cmp expect output - + test_i18ncmp expect output ' -test_expect_success C_LOCALE_OUTPUT 'status with color.status' ' - +test_expect_success 'status with color.status' ' git config color.status always && test_when_finished "git config --unset color.status" && git status | test_decode_color >output && - test_cmp expect output - + test_i18ncmp expect output ' cat >expect <<\EOF @@ -570,12 +558,12 @@ cat >expect <<\EOF EOF -test_expect_success C_LOCALE_OUTPUT 'status without relative paths' ' +test_expect_success 'status without relative paths' ' git config status.relativePaths false && test_when_finished "git config --unset status.relativePaths" && (cd dir1 && git status) >output && - test_cmp expect output + test_i18ncmp expect output ' @@ -616,11 +604,8 @@ cat <<EOF >expect # untracked EOF test_expect_success 'dry-run of partial commit excluding new file in index' ' - git commit --dry-run dir1/modified >output -' - -test_expect_success C_LOCALE_OUTPUT 'dry-run of partial commit excluding new file in index: output' ' - test_cmp expect output + git commit --dry-run dir1/modified >output && + test_i18ncmp expect output ' cat >expect <<EOF @@ -667,15 +652,15 @@ cat >expect <<EOF # output # untracked EOF -test_expect_success C_LOCALE_OUTPUT 'status submodule summary is disabled by default' ' +test_expect_success 'status submodule summary is disabled by default' ' git status >output && - test_cmp expect output + test_i18ncmp expect output ' # we expect the same as the previous test -test_expect_success C_LOCALE_OUTPUT 'status --untracked-files=all does not show submodule' ' +test_expect_success 'status --untracked-files=all does not show submodule' ' git status --untracked-files=all >output && - test_cmp expect output + test_i18ncmp expect output ' cat >expect <<EOF @@ -731,10 +716,10 @@ cat >expect <<EOF # output # untracked EOF -test_expect_success C_LOCALE_OUTPUT 'status submodule summary' ' +test_expect_success 'status submodule summary' ' git config status.submodulesummary 10 && git status >output && - test_cmp expect output + test_i18ncmp expect output ' cat >expect <<EOF @@ -773,15 +758,12 @@ cat >expect <<EOF no changes added to commit (use "git add" and/or "git commit -a") EOF test_expect_success 'status submodule summary (clean submodule): commit' ' - git commit -m "commit submodule" -' - -test_expect_success C_LOCALE_OUTPUT 'status submodule summary (clean submodule): output' ' + git commit -m "commit submodule" && git config status.submodulesummary 10 && test_must_fail git commit --dry-run >output && - test_cmp expect output && + test_i18ncmp expect output && git status >output && - test_cmp expect output + test_i18ncmp expect output ' cat >expect <<EOF @@ -827,10 +809,10 @@ cat >expect <<EOF # output # untracked EOF -test_expect_success C_LOCALE_OUTPUT 'commit --dry-run submodule summary (--amend)' ' +test_expect_success 'commit --dry-run submodule summary (--amend)' ' git config status.submodulesummary 10 && git commit --dry-run --amend >output && - test_cmp expect output + test_i18ncmp expect output ' test_expect_success POSIXPERM,SANITY 'status succeeds in a read-only repository' ' @@ -882,84 +864,84 @@ cat > expect << EOF # untracked EOF -test_expect_success C_LOCALE_OUTPUT '--ignore-submodules=untracked suppresses submodules with untracked content' ' - echo modified > sm/untracked && - git status --ignore-submodules=untracked > output && - test_cmp expect output +test_expect_success '--ignore-submodules=untracked suppresses submodules with untracked content' ' + echo modified sm/untracked && + git status --ignore-submodules=untracked >output && + test_i18ncmp expect output ' -test_expect_success C_LOCALE_OUTPUT '.gitmodules ignore=untracked suppresses submodules with untracked content' ' +test_expect_success '.gitmodules ignore=untracked suppresses submodules with untracked content' ' git config diff.ignoreSubmodules dirty && git status >output && - test_cmp expect output && + test_i18ncmp expect output && git config --add -f .gitmodules submodule.subname.ignore untracked && git config --add -f .gitmodules submodule.subname.path sm && - git status > output && - test_cmp expect output && + git status >output && + test_i18ncmp expect output && git config -f .gitmodules --remove-section submodule.subname && git config --unset diff.ignoreSubmodules ' -test_expect_success C_LOCALE_OUTPUT '.git/config ignore=untracked suppresses submodules with untracked content' ' +test_expect_success '.git/config ignore=untracked suppresses submodules with untracked content' ' git config --add -f .gitmodules submodule.subname.ignore none && git config --add -f .gitmodules submodule.subname.path sm && git config --add submodule.subname.ignore untracked && git config --add submodule.subname.path sm && - git status > output && - test_cmp expect output && + git status >output && + test_i18ncmp expect output && git config --remove-section submodule.subname && git config --remove-section -f .gitmodules submodule.subname ' -test_expect_success C_LOCALE_OUTPUT '--ignore-submodules=dirty suppresses submodules with untracked content' ' - git status --ignore-submodules=dirty > output && - test_cmp expect output +test_expect_success '--ignore-submodules=dirty suppresses submodules with untracked content' ' + git status --ignore-submodules=dirty >output && + test_i18ncmp expect output ' -test_expect_success C_LOCALE_OUTPUT '.gitmodules ignore=dirty suppresses submodules with untracked content' ' +test_expect_success '.gitmodules ignore=dirty suppresses submodules with untracked content' ' git config diff.ignoreSubmodules dirty && git status >output && ! test -s actual && git config --add -f .gitmodules submodule.subname.ignore dirty && git config --add -f .gitmodules submodule.subname.path sm && - git status > output && - test_cmp expect output && + git status >output && + test_i18ncmp expect output && git config -f .gitmodules --remove-section submodule.subname && git config --unset diff.ignoreSubmodules ' -test_expect_success C_LOCALE_OUTPUT '.git/config ignore=dirty suppresses submodules with untracked content' ' +test_expect_success '.git/config ignore=dirty suppresses submodules with untracked content' ' git config --add -f .gitmodules submodule.subname.ignore none && git config --add -f .gitmodules submodule.subname.path sm && git config --add submodule.subname.ignore dirty && git config --add submodule.subname.path sm && - git status > output && - test_cmp expect output && + git status >output && + test_i18ncmp expect output && git config --remove-section submodule.subname && git config -f .gitmodules --remove-section submodule.subname ' -test_expect_success C_LOCALE_OUTPUT '--ignore-submodules=dirty suppresses submodules with modified content' ' - echo modified > sm/foo && - git status --ignore-submodules=dirty > output && - test_cmp expect output +test_expect_success '--ignore-submodules=dirty suppresses submodules with modified content' ' + echo modified >sm/foo && + git status --ignore-submodules=dirty >output && + test_i18ncmp expect output ' -test_expect_success C_LOCALE_OUTPUT '.gitmodules ignore=dirty suppresses submodules with modified content' ' +test_expect_success '.gitmodules ignore=dirty suppresses submodules with modified content' ' git config --add -f .gitmodules submodule.subname.ignore dirty && git config --add -f .gitmodules submodule.subname.path sm && - git status > output && - test_cmp expect output && + git status >output && + test_i18ncmp expect output && git config -f .gitmodules --remove-section submodule.subname ' -test_expect_success C_LOCALE_OUTPUT '.git/config ignore=dirty suppresses submodules with modified content' ' +test_expect_success '.git/config ignore=dirty suppresses submodules with modified content' ' git config --add -f .gitmodules submodule.subname.ignore none && git config --add -f .gitmodules submodule.subname.path sm && git config --add submodule.subname.ignore dirty && git config --add submodule.subname.path sm && - git status > output && - test_cmp expect output && + git status >output && + test_i18ncmp expect output && git config --remove-section submodule.subname && git config -f .gitmodules --remove-section submodule.subname ' @@ -996,26 +978,26 @@ cat > expect << EOF # untracked EOF -test_expect_success C_LOCALE_OUTPUT "--ignore-submodules=untracked doesn't suppress submodules with modified content" ' +test_expect_success "--ignore-submodules=untracked doesn't suppress submodules with modified content" ' git status --ignore-submodules=untracked > output && - test_cmp expect output + test_i18ncmp expect output ' -test_expect_success C_LOCALE_OUTPUT ".gitmodules ignore=untracked doesn't suppress submodules with modified content" ' +test_expect_success ".gitmodules ignore=untracked doesn't suppress submodules with modified content" ' git config --add -f .gitmodules submodule.subname.ignore untracked && git config --add -f .gitmodules submodule.subname.path sm && - git status > output && - test_cmp expect output && + git status >output && + test_i18ncmp expect output && git config -f .gitmodules --remove-section submodule.subname ' -test_expect_success C_LOCALE_OUTPUT ".git/config ignore=untracked doesn't suppress submodules with modified content" ' +test_expect_success ".git/config ignore=untracked doesn't suppress submodules with modified content" ' git config --add -f .gitmodules submodule.subname.ignore none && git config --add -f .gitmodules submodule.subname.path sm && git config --add submodule.subname.ignore untracked && git config --add submodule.subname.path sm && - git status > output && - test_cmp expect output && + git status >output && + test_i18ncmp expect output && git config --remove-section submodule.subname && git config -f .gitmodules --remove-section submodule.subname ' @@ -1058,49 +1040,49 @@ cat > expect << EOF # untracked EOF -test_expect_success C_LOCALE_OUTPUT "--ignore-submodules=untracked doesn't suppress submodule summary" ' +test_expect_success "--ignore-submodules=untracked doesn't suppress submodule summary" ' git status --ignore-submodules=untracked > output && - test_cmp expect output + test_i18ncmp expect output ' -test_expect_success C_LOCALE_OUTPUT ".gitmodules ignore=untracked doesn't suppress submodule summary" ' +test_expect_success ".gitmodules ignore=untracked doesn't suppress submodule summary" ' git config --add -f .gitmodules submodule.subname.ignore untracked && git config --add -f .gitmodules submodule.subname.path sm && - git status > output && - test_cmp expect output && + git status >output && + test_i18ncmp expect output && git config -f .gitmodules --remove-section submodule.subname ' -test_expect_success C_LOCALE_OUTPUT ".git/config ignore=untracked doesn't suppress submodule summary" ' +test_expect_success ".git/config ignore=untracked doesn't suppress submodule summary" ' git config --add -f .gitmodules submodule.subname.ignore none && git config --add -f .gitmodules submodule.subname.path sm && git config --add submodule.subname.ignore untracked && git config --add submodule.subname.path sm && - git status > output && - test_cmp expect output && + git status >output && + test_i18ncmp expect output && git config --remove-section submodule.subname && git config -f .gitmodules --remove-section submodule.subname ' -test_expect_success C_LOCALE_OUTPUT "--ignore-submodules=dirty doesn't suppress submodule summary" ' +test_expect_success "--ignore-submodules=dirty doesn't suppress submodule summary" ' git status --ignore-submodules=dirty > output && - test_cmp expect output + test_i18ncmp expect output ' -test_expect_success C_LOCALE_OUTPUT ".gitmodules ignore=dirty doesn't suppress submodule summary" ' +test_expect_success ".gitmodules ignore=dirty doesn't suppress submodule summary" ' git config --add -f .gitmodules submodule.subname.ignore dirty && git config --add -f .gitmodules submodule.subname.path sm && - git status > output && - test_cmp expect output && + git status >output && + test_i18ncmp expect output && git config -f .gitmodules --remove-section submodule.subname ' -test_expect_success C_LOCALE_OUTPUT ".git/config ignore=dirty doesn't suppress submodule summary" ' +test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary" ' git config --add -f .gitmodules submodule.subname.ignore none && git config --add -f .gitmodules submodule.subname.path sm && git config --add submodule.subname.ignore dirty && git config --add submodule.subname.path sm && - git status > output && - test_cmp expect output && + git status >output && + test_i18ncmp expect output && git config --remove-section submodule.subname && git config -f .gitmodules --remove-section submodule.subname ' @@ -1126,9 +1108,9 @@ cat > expect << EOF no changes added to commit (use "git add" and/or "git commit -a") EOF -test_expect_success C_LOCALE_OUTPUT "--ignore-submodules=all suppresses submodule summary" ' +test_expect_success "--ignore-submodules=all suppresses submodule summary" ' git status --ignore-submodules=all > output && - test_cmp expect output + test_i18ncmp expect output ' test_expect_failure '.gitmodules ignore=all suppresses submodule summary' ' diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 87d5d788cb..e84e822219 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -495,10 +495,10 @@ test_expect_success 'merge fast-forward in a dirty tree' ' test_debug 'git log --graph --decorate --oneline --all' -test_expect_success C_LOCALE_OUTPUT 'in-index merge' ' +test_expect_success 'in-index merge' ' git reset --hard c0 && git merge --no-ff -s resolve c1 >out && - grep "Wonderful." out && + test_i18ngrep "Wonderful." out && verify_parents $c0 $c1 ' diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh index ef84f04102..72a8731d5e 100755 --- a/t/t7607-merge-overwrite.sh +++ b/t/t7607-merge-overwrite.sh @@ -150,11 +150,8 @@ test_expect_success 'will not overwrite untracked file on unborn branch' ' git rm -fr . && git checkout --orphan new && cp important c0.c && - test_must_fail git merge c0 2>out -' - -test_expect_success C_LOCALE_OUTPUT 'will not overwrite untracked file on unborn branch: output' ' - test_cmp out expect + test_must_fail git merge c0 2>out && + test_i18ncmp out expect ' test_expect_success 'will not overwrite untracked file on unborn branch .git/MERGE_HEAD sanity etc.' ' diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh index dc838c93bc..cbc08e3276 100755 --- a/t/t7610-mergetool.sh +++ b/t/t7610-mergetool.sh @@ -22,26 +22,50 @@ test_expect_success 'setup' ' echo master file14 >file14 && mkdir subdir && echo master sub >subdir/file3 && - git add file1 file1[1-4] subdir/file3 && + test_create_repo submod && + ( + cd submod && + : >foo && + git add foo && + git commit -m "Add foo" + ) && + git submodule add git://example.com/submod submod && + git add file1 file1[1-4] subdir/file3 .gitmodules submod && git commit -m "add initial versions" && git checkout -b branch1 master && + git submodule update -N && echo branch1 change >file1 && echo branch1 newfile >file2 && echo branch1 change file11 >file11 && echo branch1 change file13 >file13 && echo branch1 sub >subdir/file3 && - git add file1 file11 file13 file2 subdir/file3 && + ( + cd submod && + echo branch1 submodule >bar && + git add bar && + git commit -m "Add bar on branch1" && + git checkout -b submod-branch1 + ) && + git add file1 file11 file13 file2 subdir/file3 submod && git rm file12 && git commit -m "branch1 changes" && git checkout master && + git submodule update -N && echo master updated >file1 && echo master new >file2 && echo master updated file12 >file12 && echo master updated file14 >file14 && echo master new sub >subdir/file3 && - git add file1 file12 file14 file2 subdir/file3 && + ( + cd submod && + echo master submodule >bar && + git add bar && + git commit -m "Add bar on master" && + git checkout -b submod-master + ) && + git add file1 file12 file14 file2 subdir/file3 submod && git rm file11 && git commit -m "master updates" && @@ -52,15 +76,18 @@ test_expect_success 'setup' ' test_expect_success 'custom mergetool' ' git checkout -b test1 branch1 && + git submodule update -N && test_must_fail git merge master >/dev/null 2>&1 && ( yes "" | git mergetool file1 >/dev/null 2>&1 ) && ( yes "" | git mergetool file2 >/dev/null 2>&1 ) && ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) && + ( yes "l" | git mergetool submod >/dev/null 2>&1 ) && test "$(cat file1)" = "master updated" && test "$(cat file2)" = "master new" && test "$(cat subdir/file3)" = "master new sub" && + test "$(cat submod/bar)" = "branch1 submodule" && git commit -m "branch1 resolved with mergetool" ' @@ -73,9 +100,12 @@ test_expect_success 'mergetool crlf' ' ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) && + ( yes "r" | git mergetool submod >/dev/null 2>&1 ) && test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" && test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" && test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" && + git submodule update -N && + test "$(cat submod/bar)" = "master submodule" && git commit -m "branch1 resolved with mergetool - autocrlf" && git config core.autocrlf false && git reset --hard @@ -83,6 +113,7 @@ test_expect_success 'mergetool crlf' ' test_expect_success 'mergetool in subdir' ' git checkout -b test3 branch1 && + git submodule update -N && ( cd subdir && test_must_fail git merge master >/dev/null 2>&1 && @@ -98,18 +129,22 @@ test_expect_success 'mergetool on file in parent dir' ' ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) && ( yes "d" | git mergetool ../file11 >/dev/null 2>&1 ) && ( yes "d" | git mergetool ../file12 >/dev/null 2>&1 ) && + ( yes "l" | git mergetool ../submod >/dev/null 2>&1 ) && test "$(cat ../file1)" = "master updated" && test "$(cat ../file2)" = "master new" && + test "$(cat ../submod/bar)" = "branch1 submodule" && git commit -m "branch1 resolved with mergetool - subdir" ) ' test_expect_success 'mergetool skips autoresolved' ' git checkout -b test4 branch1 && + git submodule update -N && test_must_fail git merge master && test -n "$(git ls-files -u)" && ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) && ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) && + ( yes "l" | git mergetool submod >/dev/null 2>&1 ) && output="$(git mergetool --no-prompt)" && test "$output" = "No files need merging" && git reset --hard @@ -120,10 +155,13 @@ test_expect_success 'mergetool merges all from subdir' ' cd subdir && git config rerere.enabled false && test_must_fail git merge master && + ( yes "r" | git mergetool ../submod ) && ( yes "d" "d" | git mergetool --no-prompt ) && test "$(cat ../file1)" = "master updated" && test "$(cat ../file2)" = "master new" && test "$(cat file3)" = "master new sub" && + ( cd .. && git submodule update -N ) && + test "$(cat ../submod/bar)" = "master submodule" && git commit -m "branch2 resolved by mergetool from subdir" ) ' @@ -132,11 +170,257 @@ test_expect_success 'mergetool skips resolved paths when rerere is active' ' git config rerere.enabled true && rm -rf .git/rr-cache && git checkout -b test5 branch1 + git submodule update -N && test_must_fail git merge master >/dev/null 2>&1 && + ( yes "l" | git mergetool --no-prompt submod >/dev/null 2>&1 ) && ( yes "d" "d" | git mergetool --no-prompt >/dev/null 2>&1 ) && + git submodule update -N && output="$(yes "n" | git mergetool --no-prompt)" && test "$output" = "No files need merging" && git reset --hard ' +test_expect_success 'deleted vs modified submodule' ' + git checkout -b test6 branch1 && + git submodule update -N && + mv submod submod-movedaside && + git rm submod && + git commit -m "Submodule deleted from branch" && + git checkout -b test6.a test6 && + test_must_fail git merge master && + test -n "$(git ls-files -u)" && + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && + ( yes "r" | git mergetool submod ) && + rmdir submod && mv submod-movedaside submod && + test "$(cat submod/bar)" = "branch1 submodule" && + git submodule update -N && + test "$(cat submod/bar)" = "master submodule" && + output="$(git mergetool --no-prompt)" && + test "$output" = "No files need merging" && + git commit -m "Merge resolved by keeping module" && + + mv submod submod-movedaside && + git checkout -b test6.b test6 && + git submodule update -N && + test_must_fail git merge master && + test -n "$(git ls-files -u)" && + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && + ( yes "l" | git mergetool submod ) && + test ! -e submod && + output="$(git mergetool --no-prompt)" && + test "$output" = "No files need merging" && + git commit -m "Merge resolved by deleting module" && + + mv submod-movedaside submod && + git checkout -b test6.c master && + git submodule update -N && + test_must_fail git merge test6 && + test -n "$(git ls-files -u)" && + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && + ( yes "r" | git mergetool submod ) && + test ! -e submod && + test -d submod.orig && + git submodule update -N && + output="$(git mergetool --no-prompt)" && + test "$output" = "No files need merging" && + git commit -m "Merge resolved by deleting module" && + mv submod.orig submod && + + git checkout -b test6.d master && + git submodule update -N && + test_must_fail git merge test6 && + test -n "$(git ls-files -u)" && + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && + ( yes "l" | git mergetool submod ) && + test "$(cat submod/bar)" = "master submodule" && + git submodule update -N && + test "$(cat submod/bar)" = "master submodule" && + output="$(git mergetool --no-prompt)" && + test "$output" = "No files need merging" && + git commit -m "Merge resolved by keeping module" && + git reset --hard HEAD +' + +test_expect_success 'file vs modified submodule' ' + git checkout -b test7 branch1 && + git submodule update -N && + mv submod submod-movedaside && + git rm submod && + echo not a submodule >submod && + git add submod && + git commit -m "Submodule path becomes file" && + git checkout -b test7.a branch1 && + test_must_fail git merge master && + test -n "$(git ls-files -u)" && + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && + ( yes "r" | git mergetool submod ) && + rmdir submod && mv submod-movedaside submod && + test "$(cat submod/bar)" = "branch1 submodule" && + git submodule update -N && + test "$(cat submod/bar)" = "master submodule" && + output="$(git mergetool --no-prompt)" && + test "$output" = "No files need merging" && + git commit -m "Merge resolved by keeping module" && + + mv submod submod-movedaside && + git checkout -b test7.b test7 && + test_must_fail git merge master && + test -n "$(git ls-files -u)" && + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && + ( yes "l" | git mergetool submod ) && + git submodule update -N && + test "$(cat submod)" = "not a submodule" && + output="$(git mergetool --no-prompt)" && + test "$output" = "No files need merging" && + git commit -m "Merge resolved by keeping file" && + + git checkout -b test7.c master && + rmdir submod && mv submod-movedaside submod && + test ! -e submod.orig && + git submodule update -N && + test_must_fail git merge test7 && + test -n "$(git ls-files -u)" && + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && + ( yes "r" | git mergetool submod ) && + test -d submod.orig && + git submodule update -N && + test "$(cat submod)" = "not a submodule" && + output="$(git mergetool --no-prompt)" && + test "$output" = "No files need merging" && + git commit -m "Merge resolved by keeping file" && + + git checkout -b test7.d master && + rmdir submod && mv submod.orig submod && + git submodule update -N && + test_must_fail git merge test7 && + test -n "$(git ls-files -u)" && + ( yes "" | git mergetool file1 file2 subdir/file3 >/dev/null 2>&1 ) && + ( yes "d" | git mergetool file11 file12 >/dev/null 2>&1 ) && + ( yes "l" | git mergetool submod ) && + test "$(cat submod/bar)" = "master submodule" && + git submodule update -N && + test "$(cat submod/bar)" = "master submodule" && + output="$(git mergetool --no-prompt)" && + test "$output" = "No files need merging" && + git commit -m "Merge resolved by keeping module" +' + +test_expect_success 'submodule in subdirectory' ' + git checkout -b test10 branch1 && + git submodule update -N && + ( + cd subdir && + test_create_repo subdir_module && + ( + cd subdir_module && + : >file15 && + git add file15 && + git commit -m "add initial versions" + ) + ) && + git submodule add git://example.com/subsubmodule subdir/subdir_module && + git add subdir/subdir_module && + git commit -m "add submodule in subdirectory" && + + git checkout -b test10.a test10 && + git submodule update -N && + ( + cd subdir/subdir_module && + git checkout -b super10.a && + echo test10.a >file15 && + git add file15 && + git commit -m "on branch 10.a" + ) && + git add subdir/subdir_module && + git commit -m "change submodule in subdirectory on test10.a" && + + git checkout -b test10.b test10 && + git submodule update -N && + ( + cd subdir/subdir_module && + git checkout -b super10.b && + echo test10.b >file15 && + git add file15 && + git commit -m "on branch 10.b" + ) && + git add subdir/subdir_module && + git commit -m "change submodule in subdirectory on test10.b" && + + test_must_fail git merge test10.a >/dev/null 2>&1 && + ( + cd subdir && + ( yes "l" | git mergetool subdir_module ) + ) && + test "$(cat subdir/subdir_module/file15)" = "test10.b" && + git submodule update -N && + test "$(cat subdir/subdir_module/file15)" = "test10.b" && + git reset --hard && + git submodule update -N && + + test_must_fail git merge test10.a >/dev/null 2>&1 && + ( yes "r" | git mergetool subdir/subdir_module ) && + test "$(cat subdir/subdir_module/file15)" = "test10.b" && + git submodule update -N && + test "$(cat subdir/subdir_module/file15)" = "test10.a" && + git commit -m "branch1 resolved with mergetool" && + rm -rf subdir/subdir_module +' + +test_expect_success 'directory vs modified submodule' ' + git checkout -b test11 branch1 && + mv submod submod-movedaside && + git rm submod && + mkdir submod && + echo not a submodule >submod/file16 && + git add submod/file16 && + git commit -m "Submodule path becomes directory" && + + test_must_fail git merge master && + test -n "$(git ls-files -u)" && + ( yes "l" | git mergetool submod ) && + test "$(cat submod/file16)" = "not a submodule" && + rm -rf submod.orig && + + git reset --hard && + test_must_fail git merge master && + test -n "$(git ls-files -u)" && + test ! -e submod.orig && + ( yes "r" | git mergetool submod ) && + test -d submod.orig && + test "$(cat submod.orig/file16)" = "not a submodule" && + rm -r submod.orig && + mv submod-movedaside/.git submod && + ( cd submod && git clean -f && git reset --hard ) && + git submodule update -N && + test "$(cat submod/bar)" = "master submodule" && + git reset --hard && rm -rf submod-movedaside && + + git checkout -b test11.c master && + git submodule update -N && + test_must_fail git merge test11 && + test -n "$(git ls-files -u)" && + ( yes "l" | git mergetool submod ) && + git submodule update -N && + test "$(cat submod/bar)" = "master submodule" && + + git reset --hard && + git submodule update -N && + test_must_fail git merge test11 && + test -n "$(git ls-files -u)" && + test ! -e submod.orig && + ( yes "r" | git mergetool submod ) && + test "$(cat submod/file16)" = "not a submodule" && + + git reset --hard master && + ( cd submod && git clean -f && git reset --hard ) && + git submodule update -N +' + test_done diff --git a/t/t7611-merge-abort.sh b/t/t7611-merge-abort.sh index cdb3f444cd..7b4798e8e4 100755 --- a/t/t7611-merge-abort.sh +++ b/t/t7611-merge-abort.sh @@ -46,11 +46,8 @@ test_expect_success 'setup' ' pre_merge_head="$(git rev-parse HEAD)" test_expect_success 'fails without MERGE_HEAD (unstarted merge)' ' - test_must_fail git merge --abort 2>output -' - -test_expect_success C_LOCALE_OUTPUT 'fails without MERGE_HEAD (unstarted merge): fatal output' ' - grep -q MERGE_HEAD output + test_must_fail git merge --abort 2>output && + test_i18ngrep MERGE_HEAD output ' test_expect_success 'fails without MERGE_HEAD (unstarted merge): .git/MERGE_HEAD sanity' ' @@ -63,11 +60,8 @@ test_expect_success 'fails without MERGE_HEAD (completed merge)' ' test ! -f .git/MERGE_HEAD && # Merge successfully completed post_merge_head="$(git rev-parse HEAD)" && - test_must_fail git merge --abort 2>output -' - -test_expect_success C_LOCALE_OUTPUT 'fails without MERGE_HEAD (completed merge): output' ' - grep -q MERGE_HEAD output + test_must_fail git merge --abort 2>output && + test_i18ngrep MERGE_HEAD output ' test_expect_success 'fails without MERGE_HEAD (completed merge): .git/MERGE_HEAD sanity' ' diff --git a/t/t7811-grep-open.sh b/t/t7811-grep-open.sh index aedf484fee..a8957782cf 100755 --- a/t/t7811-grep-open.sh +++ b/t/t7811-grep-open.sh @@ -61,9 +61,9 @@ test_expect_success SIMPLEPAGER 'git grep -O' ' test_cmp empty out ' -test_expect_success C_LOCALE_OUTPUT 'git grep -O --cached' ' +test_expect_success 'git grep -O --cached' ' test_must_fail git grep --cached -O GREP_PATTERN >out 2>msg && - grep open-files-in-pager msg + test_i18ngrep open-files-in-pager msg ' test_expect_success 'git grep -O --no-index' ' diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh index d3a51e1269..e2896cffc1 100755 --- a/t/t8002-blame.sh +++ b/t/t8002-blame.sh @@ -8,7 +8,7 @@ PROG='git blame -c' PROG='git blame -c -e' test_expect_success 'Blame --show-email works' ' - check_count "<A@test.git>" 1 "<B@test.git>" 1 "<B1@test.git>" 1 "<B2@test.git>" 1 "<author@example.com>" 1 "<C@test.git>" 1 "<D@test.git>" 1 + check_count "<A@test.git>" 1 "<B@test.git>" 1 "<B1@test.git>" 1 "<B2@test.git>" 1 "<author@example.com>" 1 "<C@test.git>" 1 "<D@test.git>" 1 "<E at test dot git>" 1 ' test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index abc47f3abc..c5b18e282a 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -575,7 +575,7 @@ test_external () { test_external_without_stderr () { # The temporary file has no (and must have no) security # implications. - tmp="$TMPDIR"; if [ -z "$tmp" ]; then tmp=/tmp; fi + tmp=${TMPDIR:-/tmp} stderr="$tmp/git-external-stderr.$$.tmp" test_external "$@" 4> "$stderr" [ -f "$stderr" ] || error "Internal error: $stderr disappeared." @@ -801,12 +801,14 @@ test_done () { mkdir -p "$test_results_dir" test_results_path="$test_results_dir/${0%.sh}-$$.counts" - echo "total $test_count" >> $test_results_path - echo "success $test_success" >> $test_results_path - echo "fixed $test_fixed" >> $test_results_path - echo "broken $test_broken" >> $test_results_path - echo "failed $test_failure" >> $test_results_path - echo "" >> $test_results_path + cat >>"$test_results_path" <<-EOF + total $test_count + success $test_success + fixed $test_fixed + broken $test_broken + failed $test_failure + + EOF fi if test "$test_fixed" != 0 @@ -1077,6 +1079,32 @@ else test_set_prereq C_LOCALE_OUTPUT fi +# Use this instead of test_cmp to compare files that contain expected and +# actual output from git commands that can be translated. When running +# under GETTEXT_POISON this pretends that the command produced expected +# results. +test_i18ncmp () { + test -n "$GETTEXT_POISON" || test_cmp "$@" +} + +# Use this instead of "grep expected-string actual" to see if the +# output from a git command that can be translated either contains an +# expected string, or does not contain an unwanted one. When running +# under GETTEXT_POISON this pretends that the command produced expected +# results. +test_i18ngrep () { + if test -n "$GETTEXT_POISON" + then + : # pretend success + elif test "x!" = "x$1" + then + shift + ! grep "$@" + else + grep "$@" + fi +} + # test whether the filesystem supports symbolic links ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS rm -f y diff --git a/tree-diff.c b/tree-diff.c index 76f83fcc27..3f4072525b 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -64,23 +64,17 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, struct strbuf *base) { - int all_interesting = 0; - while (desc->size) { - int show; - - if (all_interesting) - show = 1; - else { - show = tree_entry_interesting(&desc->entry, base, 0, - &opt->pathspec); - if (show == 2) - all_interesting = 1; + int match = 0; + for (; desc->size; update_tree_entry(desc)) { + if (match != 2) { + match = tree_entry_interesting(&desc->entry, base, 0, + &opt->pathspec); + if (match < 0) + break; + if (match == 0) + continue; } - if (show < 0) - break; - if (show) - show_entry(opt, prefix, desc, base); - update_tree_entry(desc); + show_entry(opt, prefix, desc, base); } } @@ -120,20 +114,16 @@ static void show_entry(struct diff_options *opt, const char *prefix, } static void skip_uninteresting(struct tree_desc *t, struct strbuf *base, - struct diff_options *opt, int *all_interesting) + struct diff_options *opt, int *match) { while (t->size) { - int show = tree_entry_interesting(&t->entry, base, 0, &opt->pathspec); - if (show == 2) - *all_interesting = 1; - if (!show) { - update_tree_entry(t); - continue; + *match = tree_entry_interesting(&t->entry, base, 0, &opt->pathspec); + if (*match) { + if (*match < 0) + t->size = 0; + break; } - /* Skip it all? */ - if (show < 0) - t->size = 0; - return; + update_tree_entry(t); } } @@ -142,8 +132,7 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, { struct strbuf base; int baselen = strlen(base_str); - int all_t1_interesting = 0; - int all_t2_interesting = 0; + int t1_match = 0, t2_match = 0; /* Enable recursion indefinitely */ opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE); @@ -157,10 +146,8 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, DIFF_OPT_TST(opt, HAS_CHANGES)) break; if (opt->pathspec.nr) { - if (!all_t1_interesting) - skip_uninteresting(t1, &base, opt, &all_t1_interesting); - if (!all_t2_interesting) - skip_uninteresting(t2, &base, opt, &all_t2_interesting); + skip_uninteresting(t1, &base, opt, &t1_match); + skip_uninteresting(t2, &base, opt, &t2_match); } if (!t1->size) { if (!t2->size) diff --git a/tree-walk.c b/tree-walk.c index 322becc3b4..33f749e1e7 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -598,7 +598,7 @@ int tree_entry_interesting(const struct name_entry *entry, &never_interesting)) return 1; - if (ps->items[i].has_wildcard) { + if (ps->items[i].use_wildcard) { if (!fnmatch(match + baselen, entry->path, 0)) return 1; @@ -614,7 +614,7 @@ int tree_entry_interesting(const struct name_entry *entry, } match_wildcards: - if (!ps->items[i].has_wildcard) + if (!ps->items[i].use_wildcard) continue; /* @@ -45,62 +45,14 @@ static int read_one_entry_quick(const unsigned char *sha1, const char *base, int ADD_CACHE_JUST_APPEND); } -static int match_tree_entry(const char *base, int baselen, const char *path, unsigned int mode, const char **paths) -{ - const char *match; - int pathlen; - - if (!paths) - return 1; - pathlen = strlen(path); - while ((match = *paths++) != NULL) { - int matchlen = strlen(match); - - if (baselen >= matchlen) { - /* If it doesn't match, move along... */ - if (strncmp(base, match, matchlen)) - continue; - /* pathspecs match only at the directory boundaries */ - if (!matchlen || - baselen == matchlen || - base[matchlen] == '/' || - match[matchlen - 1] == '/') - return 1; - continue; - } - - /* Does the base match? */ - if (strncmp(base, match, baselen)) - continue; - - match += baselen; - matchlen -= baselen; - - if (pathlen > matchlen) - continue; - - if (matchlen > pathlen) { - if (match[pathlen] != '/') - continue; - if (!S_ISDIR(mode)) - continue; - } - - if (strncmp(path, match, pathlen)) - continue; - - return 1; - } - return 0; -} - -int read_tree_recursive(struct tree *tree, - const char *base, int baselen, - int stage, const char **match, - read_tree_fn_t fn, void *context) +static int read_tree_1(struct tree *tree, struct strbuf *base, + int stage, struct pathspec *pathspec, + read_tree_fn_t fn, void *context) { struct tree_desc desc; struct name_entry entry; + unsigned char sha1[20]; + int len, retval = 0, oldlen = base->len; if (parse_tree(tree)) return -1; @@ -108,10 +60,16 @@ int read_tree_recursive(struct tree *tree, init_tree_desc(&desc, tree->buffer, tree->size); while (tree_entry(&desc, &entry)) { - if (!match_tree_entry(base, baselen, entry.path, entry.mode, match)) - continue; + if (retval != 2) { + retval = tree_entry_interesting(&entry, base, 0, pathspec); + if (retval < 0) + break; + if (retval == 0) + continue; + } - switch (fn(entry.sha1, base, baselen, entry.path, entry.mode, stage, context)) { + switch (fn(entry.sha1, base->buf, base->len, + entry.path, entry.mode, stage, context)) { case 0: continue; case READ_TREE_RECURSIVE: @@ -119,56 +77,55 @@ int read_tree_recursive(struct tree *tree, default: return -1; } - if (S_ISDIR(entry.mode)) { - int retval; - char *newbase; - unsigned int pathlen = tree_entry_len(entry.path, entry.sha1); - - newbase = xmalloc(baselen + 1 + pathlen); - memcpy(newbase, base, baselen); - memcpy(newbase + baselen, entry.path, pathlen); - newbase[baselen + pathlen] = '/'; - retval = read_tree_recursive(lookup_tree(entry.sha1), - newbase, - baselen + pathlen + 1, - stage, match, fn, context); - free(newbase); - if (retval) - return -1; - continue; - } else if (S_ISGITLINK(entry.mode)) { - int retval; - struct strbuf path; - unsigned int entrylen; - struct commit *commit; - entrylen = tree_entry_len(entry.path, entry.sha1); - strbuf_init(&path, baselen + entrylen + 1); - strbuf_add(&path, base, baselen); - strbuf_add(&path, entry.path, entrylen); - strbuf_addch(&path, '/'); + if (S_ISDIR(entry.mode)) + hashcpy(sha1, entry.sha1); + else if (S_ISGITLINK(entry.mode)) { + struct commit *commit; commit = lookup_commit(entry.sha1); if (!commit) - die("Commit %s in submodule path %s not found", - sha1_to_hex(entry.sha1), path.buf); + die("Commit %s in submodule path %s%s not found", + sha1_to_hex(entry.sha1), + base->buf, entry.path); if (parse_commit(commit)) - die("Invalid commit %s in submodule path %s", - sha1_to_hex(entry.sha1), path.buf); - - retval = read_tree_recursive(commit->tree, - path.buf, path.len, - stage, match, fn, context); - strbuf_release(&path); - if (retval) - return -1; - continue; + die("Invalid commit %s in submodule path %s%s", + sha1_to_hex(entry.sha1), + base->buf, entry.path); + + hashcpy(sha1, commit->tree->object.sha1); } + else + continue; + + len = tree_entry_len(entry.path, entry.sha1); + strbuf_add(base, entry.path, len); + strbuf_addch(base, '/'); + retval = read_tree_1(lookup_tree(sha1), + base, stage, pathspec, + fn, context); + strbuf_setlen(base, oldlen); + if (retval) + return -1; } return 0; } +int read_tree_recursive(struct tree *tree, + const char *base, int baselen, + int stage, struct pathspec *pathspec, + read_tree_fn_t fn, void *context) +{ + struct strbuf sb = STRBUF_INIT; + int ret; + + strbuf_add(&sb, base, baselen); + ret = read_tree_1(tree, &sb, stage, pathspec, fn, context); + strbuf_release(&sb); + return ret; +} + static int cmp_cache_name_compare(const void *a_, const void *b_) { const struct cache_entry *ce1, *ce2; @@ -179,7 +136,7 @@ static int cmp_cache_name_compare(const void *a_, const void *b_) ce2->name, ce2->ce_flags); } -int read_tree(struct tree *tree, int stage, const char **match) +int read_tree(struct tree *tree, int stage, struct pathspec *match) { read_tree_fn_t fn = NULL; int i, err; @@ -25,9 +25,9 @@ typedef int (*read_tree_fn_t)(const unsigned char *, const char *, int, const ch extern int read_tree_recursive(struct tree *tree, const char *base, int baselen, - int stage, const char **match, + int stage, struct pathspec *pathspec, read_tree_fn_t fn, void *context); -extern int read_tree(struct tree *tree, int stage, const char **paths); +extern int read_tree(struct tree *tree, int stage, struct pathspec *pathspec); #endif /* TREE_H */ |
