diff options
211 files changed, 4264 insertions, 1835 deletions
diff --git a/Documentation/RelNotes-1.6.3.2.txt b/Documentation/RelNotes-1.6.3.2.txt index a3fceebb11..b2f3f0293c 100644 --- a/Documentation/RelNotes-1.6.3.2.txt +++ b/Documentation/RelNotes-1.6.3.2.txt @@ -8,44 +8,54 @@ Fixes since v1.6.3.1 casting the (char *) pointer to (int *); GCC 4.4 did not like this, and aborted compilation. - * http-push had a small use-after-free bug. - - * command completion code in bash did not reliably detect that we are - in a bare repository. - - * "git for-each-ref" had a segfaulting bug when dealing with a tag object - created by an ancient git. - * Some unlink(2) failures went undiagnosed. * The "recursive" merge strategy misbehaved when faced rename/delete conflicts while coming up with an intermediate merge base. + * The low-level merge algorithm did not handle a degenerate case of + merging a file with itself using itself as the common ancestor + gracefully. It should produce the file itself, but instead + produced an empty result. + * GIT_TRACE mechanism segfaulted when tracing a shell-quoted aliases. + * OpenBSD also uses st_ctimspec in "struct stat", instead of "st_ctim". + + * With NO_CROSS_DIRECTORY_HARDLINKS, "make install" can be told not to + create hardlinks between $(gitexecdir)/git-$builtin_commands and + $(bindir)/git. + + * command completion code in bash did not reliably detect that we are + in a bare repository. + * "git add ." in an empty directory complained that pathspec "." did not match anything, which may be technically correct, but not useful. We silently make it a no-op now. + * "git add -p" (and "patch" action in "git add -i") was broken when + the first hunk that adds a line at the top was split into two and + both halves are marked to be used. + + * "git blame path" misbehaved at the commit where path became file + from a directory with some files in it. + + * "git for-each-ref" had a segfaulting bug when dealing with a tag object + created by an ancient git. + * "git format-patch -k" still added patch numbers if format.numbered configuration was set. - * OpenBSD also uses st_ctimspec in "struct stat", instead of "st_ctim". + * "git grep --color ''" did not terminate. The command also had + subtle bugs with its -w option. - * With NO_CROSS_DIRECTORY_HARDLINKS, "make install" can be told not to - create hardlinks between $(gitexecdir)/git-$builtin_commands and - $(bindir)/git. + * http-push had a small use-after-free bug. * "git push" was converting OFS_DELTA pack representation into less efficient REF_DELTA representation unconditionally upon transfer, making the transferred data unnecessarily larger. + * "git remote show origin" segfaulted when origin was still empty. + Many other general usability updates around help text, diagnostic messages and documentation are included as well. - ---- -exec >/var/tmp/1 -O=v1.6.3.1-51-g2a1feb9 -echo O=$(git describe maint) -git shortlog --no-merges $O..maint - diff --git a/Documentation/RelNotes-1.6.4.txt b/Documentation/RelNotes-1.6.4.txt new file mode 100644 index 0000000000..af68297af5 --- /dev/null +++ b/Documentation/RelNotes-1.6.4.txt @@ -0,0 +1,93 @@ +GIT v1.6.4 Release Notes +======================== + +With the next major release, "git push" into a branch that is +currently checked out will be refused by default. You can choose +what should happen upon such a push by setting the configuration +variable receive.denyCurrentBranch in the receiving repository. + +To ease the transition plan, the receiving repository of such a +push running this release will issue a big warning when the +configuration variable is missing. Please refer to: + + http://git.or.cz/gitwiki/GitFaq#non-bare + http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007 + +for more details on the reason why this change is needed and the +transition plan. + +For a similar reason, "git push $there :$killed" to delete the branch +$killed in a remote repository $there, if $killed branch is the current +branch pointed at by its HEAD, gets a large warning. You can choose what +should happen upon such a push by setting the configuration variable +receive.denyDeleteCurrent in the receiving repository. + +When the user does not tell "git push" what to push, it has always +pushed matching refs. For some people it is unexpected, and a new +configuration variable push.default has been introduced to allow +changing a different default behaviour. To advertise the new feature, +a big warning is issued if this is not configured and a git push without +arguments is attempted. + + +Updates since v1.6.3 +-------------------- + +(subsystems) + + * gitweb Perl style clean-up. + + * git-svn updates, including a new --authors-prog option to map author + names by invoking an external program. + +(portability) + + * We feed iconv with "UTF-8" instead of "utf8"; the former is + understood more widely. + +(performance) + +(usability, bells and whistles) + + * "git add --edit" lets users edit the whole patch text to fine-tune what + is added to the index. + + * "git log --graph" draws graphs more compactly by using horizonal lines + when able. + + * "git log --decorate" shows shorter refnames by stripping well-known + refs/* prefix. + + * "git send-email" understands quoted aliases in .mailrc files (might + have to be backported to 1.6.3.X). + + * "git send-email" can fetch the sender address from the configuration + variable "sendmail.from" (and "sendmail.<identity>.from"). + + * "git show-branch" can color its output. + + * "add" and "update" subcommands to "git submodule" learned --reference + option to use local clone with references. + +(developers) + + * A major part of the "git bisect" wrapper has moved to C. + +Fixes since v1.6.3 +------------------ + +All of the fixes in v1.6.3.X maintenance series are included in this +release, unless otherwise noted. + +Here are fixes that this release has, but have not been backported to +v1.6.3.X series. + + * The way Git.pm sets up a Repository object was not friendly to callers + that chdir around. It now internally records the repository location + as an absolute path when autodetected. + +--- +exec >/var/tmp/1 +echo O=$(git describe master) +O=v1.6.3.1-168-g23807fa +git shortlog --no-merges $O..master ^maint diff --git a/Documentation/config.txt b/Documentation/config.txt index 5dcad94f84..3a86d1f8f0 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -438,6 +438,11 @@ On some file system/operating system combinations, this is unreliable. Set this config setting to 'rename' there; However, This will remove the check that makes sure that existing object files will not get overwritten. +add.ignore-errors:: + Tells 'git-add' to continue adding files when some files cannot be + added due to indexing errors. Equivalent to the '--ignore-errors' + option of linkgit:git-add[1]. + alias.*:: Command aliases for the linkgit:git[1] command wrapper - e.g. after defining "alias.last = cat-file commit HEAD", the invocation @@ -604,6 +609,12 @@ color.pager:: A boolean to enable/disable colored output when the pager is in use (default is true). +color.showbranch:: + A boolean to enable/disable color in the output of + linkgit:git-show-branch[1]. May be set to `always`, + `false` (or `never`) or `auto` (or `true`), in which case colors are used + only when the output is to a terminal. Defaults to false. + color.status:: A boolean to enable/disable color in the output of linkgit:git-status[1]. May be set to `always`, diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index d938b42289..ab1943c712 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p] - [--all | [--update | -u]] [--intent-to-add | -N] + [--edit | -e] [--all | [--update | -u]] [--intent-to-add | -N] [--refresh] [--ignore-errors] [--] <filepattern>... DESCRIPTION @@ -76,6 +76,15 @@ OPTIONS bypassed and the 'patch' subcommand is invoked using each of the specified filepatterns before exiting. +-e, \--edit:: + Open the diff vs. the index in an editor and let the user + edit it. After the editor was closed, adjust the hunk headers + and apply the patch to the index. ++ +*NOTE*: Obviously, if you change anything else than the first character +on lines beginning with a space or a minus, the patch will no longer +apply. + -u:: --update:: Update only files that git already knows about, staging modified diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index 9e5baa2777..735374d7df 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -3,7 +3,7 @@ git-apply(1) NAME ---- -git-apply - Apply a patch on a git index file and a working tree +git-apply - Apply a patch on a git index file and/or a working tree SYNOPSIS diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index cbd4275871..ae201deb7a 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -111,6 +111,7 @@ OPTIONS --no-abbrev:: Display the full sha1s in the output listing rather than abbreviating them. +-t:: --track:: When creating a new branch, set up configuration to mark the start-point branch as "upstream" from the new branch. This diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt index c1ce26884e..0b7982ea76 100644 --- a/Documentation/git-check-ref-format.txt +++ b/Documentation/git-check-ref-format.txt @@ -25,6 +25,10 @@ imposes the following rules on how references are named: grouping, but no slash-separated component can begin with a dot `.`. +. They must contain at least one `/`. This enforces the presence of a + category like `heads/`, `tags/` etc. but the actual names are not + restricted. + . They cannot have two consecutive dots `..` anywhere. . They cannot have ASCII control characters (i.e. bytes whose @@ -38,6 +42,8 @@ imposes the following rules on how references are named: . They cannot contain a sequence `@{`. +- They cannot contain a `\\`. + These rules make it easy for shell script based tools to parse reference names, pathname expansion by the shell when a reference name is used unquoted (by mistake), and also avoids ambiguities in certain diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt index 15b247bab4..96a6c51a4b 100644 --- a/Documentation/git-difftool.txt +++ b/Documentation/git-difftool.txt @@ -31,7 +31,7 @@ OPTIONS Use the diff tool specified by <tool>. Valid merge tools are: kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, - ecmerge, diffuse and opendiff + ecmerge, diffuse, opendiff and araxis. + If a diff tool is not specified, 'git-difftool' will use the configuration variable `diff.tool`. If the diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt index ff9700d17a..68ed6c0956 100644 --- a/Documentation/git-mergetool.txt +++ b/Documentation/git-mergetool.txt @@ -27,7 +27,7 @@ OPTIONS Use the merge resolution program specified by <tool>. Valid merge tools are: kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, - diffuse, tortoisemerge and opendiff + diffuse, tortoisemerge, opendiff and araxis. + If a merge resolution program is not specified, 'git-mergetool' will use the configuration variable `merge.tool`. If the diff --git a/Documentation/git-mktree.txt b/Documentation/git-mktree.txt index af19f06ed7..81e3326772 100644 --- a/Documentation/git-mktree.txt +++ b/Documentation/git-mktree.txt @@ -8,12 +8,13 @@ git-mktree - Build a tree-object from ls-tree formatted text SYNOPSIS -------- -'git mktree' [-z] +'git mktree' [-z] [--missing] [--batch] DESCRIPTION ----------- -Reads standard input in non-recursive `ls-tree` output format, -and creates a tree object. The object name of the tree object +Reads standard input in non-recursive `ls-tree` output format, and creates +a tree object. The order of the tree entries is normalised by mktree so +pre-sorting the input is not required. The object name of the tree object built is written to the standard output. OPTIONS @@ -21,6 +22,18 @@ OPTIONS -z:: Read the NUL-terminated `ls-tree -z` output instead. +--missing:: + Allow missing objects. The default behaviour (without this option) + is to verify that each tree entry's sha1 identifies an existing + object. This option has no effect on the treatment of gitlink entries + (aka "submodules") which are always allowed to be missing. + +--batch:: + Allow building of more than one tree object before exiting. Each + tree is separated by as single blank line. The final new-line is + optional. Note - if the '-z' option is used, lines are terminated + with NUL. + Author ------ Written by Junio C Hamano <gitster@pobox.com> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 3d5a066c31..26f3b7b2b0 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -231,8 +231,7 @@ OPTIONS -s <strategy>:: --strategy=<strategy>:: - Use the given merge strategy; can be supplied more than - once to specify them in the order they should be tried. + Use the given merge strategy. If there is no `-s` option, a built-in list of strategies is used instead ('git-merge-recursive' when merging a single head, 'git-merge-octopus' otherwise). This implies --merge. diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index aaa8852629..c9257a10c9 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -31,11 +31,14 @@ OPTIONS Instead of incrementally packing the unpacked objects, pack everything referenced into a single pack. Especially useful when packing a repository that is used - for private development and there is no need to worry - about people fetching via dumb protocols from it. Use + for private development. Use with '-d'. This will clean up the objects that `git prune` leaves behind, but `git fsck --full` shows as dangling. ++ +Note that users fetching over dumb protocols will have to fetch the +whole new pack in order to get any contained object, no matter how many +other objects in that pack they already have locally. -A:: Same as `-a`, unless '-d' is used. Then any unreachable diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 52c353e674..4bbdd056da 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -30,6 +30,11 @@ OPTIONS Only meaningful in `--parseopt` mode. Tells the option parser to echo out the first `--` met instead of skipping it. +--sq-quote:: + Use 'git-rev-parse' in shell quoting mode (see SQ-QUOTE + section below). In contrast to the `--sq` option below, this + mode does only quoting. Nothing else is done to command input. + --revs-only:: Do not output flags and parameters not meant for 'git-rev-list' command. @@ -64,7 +69,8 @@ OPTIONS properly quoted for consumption by shell. Useful when you expect your parameter to contain whitespaces and newlines (e.g. when using pickaxe `-S` with - 'git-diff-\*'). + 'git-diff-\*'). In contrast to the `--sq-quote` option, + the command input is still interpreted as usual. --not:: When showing object names, prefix them with '{caret}' and @@ -406,6 +412,33 @@ C? option C with an optional argument" eval `echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?` ------------ +SQ-QUOTE +-------- + +In `--sq-quote` mode, 'git-rev-parse' echoes on the standard output a +single line suitable for `sh(1)` `eval`. This line is made by +normalizing the arguments following `--sq-quote`. Nothing other than +quoting the arguments is done. + +If you want command input to still be interpreted as usual by +'git-rev-parse' before the output is shell quoted, see the `--sq` +option. + +Example +~~~~~~~ + +------------ +$ cat >your-git-script.sh <<\EOF +#!/bin/sh +args=$(git rev-parse --sq-quote "$@") # quote user-supplied arguments +command="git frotz -n24 $args" # and use it inside a handcrafted + # command line +eval "$command" +EOF + +$ sh your-git-script.sh "a b'c" +------------ + EXAMPLES -------- diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 794224b1b3..e7cb0e6c71 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -14,6 +14,10 @@ SYNOPSIS DESCRIPTION ----------- Takes the patches given on the command line and emails them out. +Patches can be specified as files, directories (which will send all +files in the directory), or directly as a revision list. In the +last case, any format accepted by linkgit:git-format-patch[1] can +be passed to git send-email. The header of the email is configurable by command line options. If not specified on the command line, the user will be prompted with a ReadLine @@ -39,6 +43,10 @@ OPTIONS Composing ~~~~~~~~~ +--annotate:: + Review and edit each patch you're about to send. See the + CONFIGURATION section for 'sendemail.multiedit'. + --bcc=<address>:: Specify a "Bcc:" value for each email. Default is the value of 'sendemail.bcc'. @@ -51,11 +59,6 @@ The --bcc option must be repeated for each user you want on the bcc list. + The --cc option must be repeated for each user you want on the cc list. ---annotate:: - Review each patch you're about to send in an editor. The setting - 'sendemail.multiedit' defines if this will spawn one editor per patch - or one for all of them at once. - --compose:: Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an introductory message for the patch series. @@ -67,11 +70,16 @@ In-Reply-To headers specified in the message. If the body of the message and In-Reply-To headers will be used unless they are removed. + Missing From or In-Reply-To headers will be prompted for. ++ +See the CONFIGURATION section for 'sendemail.multiedit'. --from=<address>:: - Specify the sender of the emails. This will default to - the value GIT_COMMITTER_IDENT, as returned by "git var -l". - The user will still be prompted to confirm this entry. + Specify the sender of the emails. If not specified on the command line, + the value of the 'sendemail.from' configuration option is used. If + neither the command line option nor 'sendemail.from' are set, then the + user will be prompted for the value. The default for the prompt will be + the value of GIT_AUTHOR_IDENT, or GIT_COMMITTER_IDENT if that is not + set, as returned by "git var -l". --in-reply-to=<identifier>:: Specify the contents of the first In-Reply-To header. @@ -135,7 +143,9 @@ user is prompted for a password while the input is masked for privacy. --smtp-server-port=<port>:: Specifies a port different from the default port (SMTP servers typically listen to smtp port 25 and ssmtp port - 465). This can be set with 'sendemail.smtpserverport'. + 465); symbolic port names (e.g. "submission" instead of 465) + are also accepted. The port can also be set with the + 'sendemail.smtpserverport' configuration variable. --smtp-ssl:: Legacy alias for '--smtp-encryption ssl'. @@ -230,6 +240,12 @@ have been specified, in which case default to 'compose'. --dry-run:: Do everything except actually send the emails. +--[no-]format-patch:: + When an argument may be understood either as a reference or as a file name, + choose to understand it as a format-patch argument ('--format-patch') + or as a file name ('--no-format-patch'). By default, when such a conflict + occurs, git send-email will fail. + --quiet:: Make git-send-email less verbose. One line per email should be all that is output. @@ -246,12 +262,6 @@ have been specified, in which case default to 'compose'. Default is the value of 'sendemail.validate'; if this is not set, default to '--validate'. ---[no-]format-patch:: - When an argument may be understood either as a reference or as a file name, - choose to understand it as a format-patch argument ('--format-patch') - or as a file name ('--no-format-patch'). By default, when such a conflict - occurs, git send-email will fail. - CONFIGURATION ------------- diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt index 51a4e9d6d7..89ec5364ec 100644 --- a/Documentation/git-show-branch.txt +++ b/Documentation/git-show-branch.txt @@ -8,9 +8,11 @@ git-show-branch - Show branches and their commits SYNOPSIS -------- [verse] -'git show-branch' [--all] [--remotes] [--topo-order] [--current] +'git show-branch' [--all] [--remotes] [--topo-order | --date-order] + [--current] [--color | --no-color] [--more=<n> | --list | --independent | --merge-base] - [--no-name | --sha1-name] [--topics] [<rev> | <glob>]... + [--no-name | --sha1-name] [--topics] + [<rev> | <glob>]... 'git show-branch' (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>] DESCRIPTION @@ -57,6 +59,11 @@ OPTIONS appear in topological order (i.e., descendant commits are shown before their parents). +--date-order:: + This option is similar to '--topo-order' in the sense that no + parent comes before all of its children, but otherwise commits + are ordered according to their commit date. + --sparse:: By default, the output omits merges that are reachable from only one tip being shown. This option makes them @@ -107,6 +114,14 @@ OPTIONS When no explicit <ref> parameter is given, it defaults to the current branch (or `HEAD` if it is detached). +--color:: + Color the status sign (one of these: `*` `!` `+` `-`) of each commit + corresponding to the branch it's in. + +--no-color:: + Turn off colored output, even when the configuration file gives the + default to color output. + Note that --more, --list, --independent and --merge-base options are mutually exclusive. diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 051f94d26f..a42d4c85bd 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -9,8 +9,8 @@ SYNOPSIS -------- [verse] 'git stash' list [<options>] -'git stash' (show | drop | pop ) [<stash>] -'git stash' apply [--index] [<stash>] +'git stash' ( show | drop ) [<stash>] +'git stash' ( pop | apply ) [--index] [<stash>] 'git stash' branch <branchname> [<stash>] 'git stash' [save [--keep-index] [<message>]] 'git stash' clear @@ -75,19 +75,27 @@ show [<stash>]:: it will accept any format known to 'git-diff' (e.g., `git stash show -p stash@\{1}` to view the second most recent stash in patch form). -apply [--index] [<stash>]:: +pop [<stash>]:: - Restore the changes recorded in the stash on top of the current - working tree state. When no `<stash>` is given, applies the latest - one. The working directory must match the index. + Remove a single stashed state from the stash list and apply it + on top of the current working tree state, i.e., do the inverse + operation of `git stash save`. The working directory must + match the index. + -This operation can fail with conflicts; you need to resolve them -by hand in the working tree. +Applying the state can fail with conflicts; in this case, it is not +removed from the stash list. You need to resolve the conflicts by hand +and call `git stash drop` manually afterwards. + If the `--index` option is used, then tries to reinstate not only the working tree's changes, but also the index's ones. However, this can fail, when you have conflicts (which are stored in the index, where you therefore can no longer apply the changes as they were originally). ++ +When no `<stash>` is given, `stash@\{0}` is assumed. + +apply [--index] [<stash>]:: + + Like `pop`, but do not remove the state from the stash list. branch <branchname> [<stash>]:: @@ -112,12 +120,6 @@ drop [<stash>]:: Remove a single stashed state from the stash list. When no `<stash>` is given, it removes the latest one. i.e. `stash@\{0}` -pop [<stash>]:: - - Remove a single stashed state from the stash list and apply on top - of the current working tree state. When no `<stash>` is given, - `stash@\{0}` is assumed. See also `apply`. - create:: Create a stash (which is a regular commit object) and return its @@ -163,7 +165,7 @@ $ git pull file foobar not up to date, cannot merge. $ git stash $ git pull -$ git stash apply +$ git stash pop ---------------------------------------------------------------- Interrupted workflow:: @@ -192,7 +194,7 @@ You can use 'git-stash' to simplify the above, like this: $ git stash $ edit emergency fix $ git commit -a -m "Fix in a hurry" -$ git stash apply +$ git stash pop # ... continue hacking ... ---------------------------------------------------------------- diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 3b8df44673..cd8e861ce4 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -9,10 +9,12 @@ git-submodule - Initialize, update or inspect submodules SYNOPSIS -------- [verse] -'git submodule' [--quiet] add [-b branch] [--] <repository> <path> +'git submodule' [--quiet] add [-b branch] + [--reference <repository>] [--] <repository> <path> 'git submodule' [--quiet] status [--cached] [--] [<path>...] 'git submodule' [--quiet] init [--] [<path>...] -'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--] [<path>...] +'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase] + [--reference <repository>] [--] [<path>...] 'git submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...] 'git submodule' [--quiet] foreach <command> 'git submodule' [--quiet] sync [--] [<path>...] @@ -113,7 +115,8 @@ init:: update:: Update the registered submodules, i.e. clone missing submodules and checkout the commit specified in the index of the containing repository. - This will make the submodules HEAD be detached. + This will make the submodules HEAD be detached unless '--rebase' is + specified or the key `submodule.$name.update` is set to `rebase`. + If the submodule is not yet initialized, and you just want to use the setting as stored in .gitmodules, you can automatically initialize the @@ -177,6 +180,23 @@ OPTIONS This option is only valid for the update command. Don't fetch new objects from the remote site. +--rebase:: + This option is only valid for the update command. + Rebase the current branch onto the commit recorded in the + superproject. If this option is given, the submodule's HEAD will not + be detached. If a a merge failure prevents this process, you will have + to resolve these failures with linkgit:git-rebase[1]. + If the key `submodule.$name.update` is set to `rebase`, this option is + implicit. + +--reference <repository>:: + This option is only valid for add and update commands. These + commands sometimes need to clone a remote repository. In this case, + this option will be passed to the linkgit:git-clone[1] command. ++ +*NOTE*: Do *not* use this option unless you have read the note +for linkgit:git-clone[1]'s --reference and --shared options carefully. + <path>...:: Paths to submodule(s). When specified this will restrict the command to only operate on the submodules found at the specified paths. diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 1c40894669..ca3fc3de1f 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -398,6 +398,14 @@ after the authors-file is modified should continue operation. config key: svn.authorsfile +--authors-prog=<filename>:: + +If this option is specified, for each SVN committer name that does not +exist in the authors file, the given file is executed with the committer +name as the first argument. The program is expected to return a single +line of the form "Name <email>", which will be treated as if included in +the authors file. + -q:: --quiet:: Make 'git-svn' less verbose. Specify a second time to make it diff --git a/Documentation/git.txt b/Documentation/git.txt index 9d8f236fe8..56d47709ac 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,7 +43,12 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.6.3/git.html[documentation for release 1.6.3] +* link:v1.6.3.2/git.html[documentation for release 1.6.3.2] + +* release notes for + link:RelNotes-1.6.3.2.txt[1.6.3.2], + link:RelNotes-1.6.3.1.txt[1.6.3.1], + link:RelNotes-1.6.3.txt[1.6.3]. * release notes for link:RelNotes-1.6.2.5.txt[1.6.2.5], @@ -227,6 +232,8 @@ The link:user-manual.html#git-concepts[git concepts chapter of the user-manual] and linkgit:gitcore-tutorial[7] both provide introductions to the underlying git architecture. +See linkgit:gitworkflows[7] for an overview of recommended workflows. + See also the link:howto-index.html[howto] documents for some useful examples. @@ -644,7 +651,8 @@ SEE ALSO linkgit:gittutorial[7], linkgit:gittutorial-2[7], link:everyday.html[Everyday Git], linkgit:gitcvs-migration[7], linkgit:gitglossary[7], linkgit:gitcore-tutorial[7], -linkgit:gitcli[7], link:user-manual.html[The Git User's Manual] +linkgit:gitcli[7], link:user-manual.html[The Git User's Manual], +linkgit:gitworkflows[7] GIT --- diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt index d1a17e2625..1b67f0a9f1 100644 --- a/Documentation/gitmodules.txt +++ b/Documentation/gitmodules.txt @@ -30,6 +30,15 @@ submodule.<name>.path:: submodule.<name>.url:: Defines an url from where the submodule repository can be cloned. +submodule.<name>.update:: + Defines what to do when the submodule is updated by the superproject. + If 'checkout' (the default), the new commit specified in the + superproject will be checked out in the submodule on a detached HEAD. + If 'rebase', the current branch of the submodule will be rebased onto + the commit specified in the superproject. + This config option is overridden if 'git submodule update' is given + the '--rebase' option. + EXAMPLES -------- diff --git a/Documentation/gittutorial.txt b/Documentation/gittutorial.txt index c5d5596d89..c7fa949c28 100644 --- a/Documentation/gittutorial.txt +++ b/Documentation/gittutorial.txt @@ -650,6 +650,9 @@ digressions that may be interesting at this point are: smart enough to perform a close-to-optimal search even in the case of complex non-linear history with lots of merged branches. + * linkgit:gitworkflows[7]: Gives an overview of recommended + workflows. + * link:everyday.html[Everyday GIT with 20 Commands Or So] * linkgit:gitcvs-migration[7]: Git for CVS users. @@ -661,6 +664,7 @@ linkgit:gitcvs-migration[7], linkgit:gitcore-tutorial[7], linkgit:gitglossary[7], linkgit:git-help[1], +linkgit:gitworkflows[7], link:everyday.html[Everyday git], link:user-manual.html[The Git User's Manual] diff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt index 4832bc75e2..c0f96e7070 100644 --- a/Documentation/merge-config.txt +++ b/Documentation/merge-config.txt @@ -23,7 +23,7 @@ merge.tool:: Controls which merge resolution program is used by linkgit:git-mergetool[1]. Valid built-in values are: "kdiff3", "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", - "diffuse", "ecmerge", "tortoisemerge", and + "diffuse", "ecmerge", "tortoisemerge", "araxis", and "opendiff". Any other value is treated is custom merge tool and there must be a corresponding mergetool.<tool>.cmd option. diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt index e30c602f47..50f9e9ac17 100644 --- a/Documentation/technical/api-parse-options.txt +++ b/Documentation/technical/api-parse-options.txt @@ -60,13 +60,13 @@ Steps to parse options . in `cmd_foo(int argc, const char **argv, const char *prefix)` call - argc = parse_options(argc, argv, builtin_foo_options, builtin_foo_usage, flags); + argc = parse_options(argc, argv, prefix, builtin_foo_options, builtin_foo_usage, flags); + `parse_options()` will filter out the processed options of `argv[]` and leave the non-option arguments in `argv[]`. `argc` is updated appropriately because of the assignment. + -You can also pass NULL instead of a usage array as fourth parameter of +You can also pass NULL instead of a usage array as the fifth parameter of parse_options(), to avoid displaying a help screen with usage info and option list. This should only be done if necessary, e.g. to implement a limited parser for only a subset of the options that needs to be run @@ -137,6 +137,10 @@ There are some macros to easily define options: Introduce a boolean option. If used, `int_var` is bitwise-ored with `mask`. +`OPT_NEGBIT(short, long, &int_var, description, mask)`:: + Introduce a boolean option. + If used, `int_var` is bitwise-anded with the inverted `mask`. + `OPT_SET_INT(short, long, &int_var, description, integer)`:: Introduce a boolean option. If used, set `int_var` to `integer`. @@ -163,9 +167,22 @@ There are some macros to easily define options: and the result will be put into `var`. See 'Option Callbacks' below for a more elaborate description. +`OPT_FILENAME(short, long, &var, description)`:: + Introduce an option with a filename argument. + The filename will be prefixed by passing the filename along with + the prefix argument of `parse_options()` to `prefix_filename()`. + `OPT_ARGUMENT(long, description)`:: Introduce a long-option argument that will be kept in `argv[]`. +`OPT_NUMBER_CALLBACK(&var, description, func_ptr)`:: + Recognize numerical options like -123 and feed the integer as + if it was an argument to the function given by `func_ptr`. + The result will be put into `var`. There can be only one such + option definition. It cannot be negated and it takes no + arguments. Short options that happen to be digits take + precedence over it. + The last element of the array must be `OPT_END()`. diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index dbbeb7e7c7..0b88a51d0b 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1520,10 +1520,10 @@ $ git commit -a -m "blorpl: typofix" ------------------------------------------------ After that, you can go back to what you were working on with -`git stash apply`: +`git stash pop`: ------------------------------------------------ -$ git stash apply +$ git stash pop ------------------------------------------------ diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index d292e3a2d3..39cde784c9 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.6.3.1 +DEF_VER=v1.6.3.GIT LF=' ' @@ -3,6 +3,11 @@ all:: # Define V=1 to have a more verbose compile. # +# Define SHELL_PATH to a POSIX shell if your /bin/sh is broken. +# +# Define SANE_TOOL_PATH to a colon-separated list of paths to prepend +# to PATH if your tools in /usr/bin are broken. +# # Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf() # or vsnprintf() return -1 instead of number of characters which would # have been written to the final string if enough space had been available. @@ -52,6 +57,10 @@ all:: # # Define NO_MKDTEMP if you don't have mkdtemp in the C library. # +# Define NO_MKSTEMPS if you don't have mkstemps in the C library. +# +# Define NO_LIBGEN_H if you don't have libgen.h. +# # Define NO_SYS_SELECT_H if you don't have sys/select.h. # # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link. @@ -91,6 +100,10 @@ all:: # Define NEEDS_SOCKET if linking with libc is not enough (SunOS, # Patrick Mauritz). # +# Define NEEDS_RESOLV if linking with -lnsl and/or -lsocket is not enough. +# Notably on Solaris hstrerror resides in libresolv and on Solaris 7 +# inet_ntop and inet_pton additionally reside there. +# # Define NO_MMAP if you want to avoid mmap. # # Define NO_PTHREADS if you do not have or do not want to use Pthreads. @@ -228,6 +241,7 @@ ETC_GITCONFIG = etc/gitconfig endif lib = lib # DESTDIR= +pathsep = : # default configuration for gitweb GITWEB_CONFIG = gitweb_config.perl @@ -335,7 +349,6 @@ PROGRAMS += git-index-pack$X PROGRAMS += git-merge-index$X PROGRAMS += git-merge-tree$X PROGRAMS += git-mktag$X -PROGRAMS += git-mktree$X PROGRAMS += git-pack-redundant$X PROGRAMS += git-patch-id$X PROGRAMS += git-shell$X @@ -589,6 +602,7 @@ BUILTIN_OBJS += builtin-merge-base.o BUILTIN_OBJS += builtin-merge-file.o BUILTIN_OBJS += builtin-merge-ours.o BUILTIN_OBJS += builtin-merge-recursive.o +BUILTIN_OBJS += builtin-mktree.o BUILTIN_OBJS += builtin-mv.o BUILTIN_OBJS += builtin-name-rev.o BUILTIN_OBJS += builtin-pack-objects.o @@ -635,10 +649,12 @@ EXTLIBS = ifeq ($(uname_S),Linux) NO_STRLCPY = YesPlease + NO_MKSTEMPS = YesPlease THREADED_DELTA_SEARCH = YesPlease endif ifeq ($(uname_S),GNU/kFreeBSD) NO_STRLCPY = YesPlease + NO_MKSTEMPS = YesPlease THREADED_DELTA_SEARCH = YesPlease endif ifeq ($(uname_S),UnixWare) @@ -650,6 +666,7 @@ ifeq ($(uname_S),UnixWare) SHELL_PATH = /usr/local/bin/bash NO_IPV6 = YesPlease NO_HSTRERROR = YesPlease + NO_MKSTEMPS = YesPlease BASIC_CFLAGS += -Kthread BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib @@ -673,6 +690,7 @@ ifeq ($(uname_S),SCO_SV) SHELL_PATH = /usr/bin/bash NO_IPV6 = YesPlease NO_HSTRERROR = YesPlease + NO_MKSTEMPS = YesPlease BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib NO_STRCASESTR = YesPlease @@ -697,11 +715,21 @@ ifeq ($(uname_S),SunOS) NEEDS_SOCKET = YesPlease NEEDS_NSL = YesPlease SHELL_PATH = /bin/bash + SANE_TOOL_PATH = /usr/xpg6/bin:/usr/xpg4/bin NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease - NO_HSTRERROR = YesPlease NO_MKDTEMP = YesPlease - OLD_ICONV = UnfortunatelyYes + NO_MKSTEMPS = YesPlease + ifeq ($(uname_R),5.7) + NEEDS_RESOLV = YesPlease + NO_IPV6 = YesPlease + NO_SOCKADDR_STORAGE = YesPlease + NO_UNSETENV = YesPlease + NO_SETENV = YesPlease + NO_STRLCPY = YesPlease + NO_C99_FORMAT = YesPlease + NO_STRTOUMAX = YesPlease + endif ifeq ($(uname_R),5.8) NO_UNSETENV = YesPlease NO_SETENV = YesPlease @@ -714,15 +742,19 @@ ifeq ($(uname_S),SunOS) NO_C99_FORMAT = YesPlease NO_STRTOUMAX = YesPlease endif - INSTALL = ginstall + ifdef NO_IPV6 + NEEDS_RESOLV = YesPlease + endif + INSTALL = /usr/ucb/install TAR = gtar - BASIC_CFLAGS += -D__EXTENSIONS__ + BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ endif ifeq ($(uname_O),Cygwin) NO_D_TYPE_IN_DIRENT = YesPlease NO_D_INO_IN_DIRENT = YesPlease NO_STRCASESTR = YesPlease NO_MEMMEM = YesPlease + NO_MKSTEMPS = YesPlease NO_SYMLINK_HEAD = YesPlease NEEDS_LIBICONV = YesPlease NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes @@ -766,11 +798,13 @@ ifeq ($(uname_S),NetBSD) BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib THREADED_DELTA_SEARCH = YesPlease USE_ST_TIMESPEC = YesPlease + NO_MKSTEMPS = YesPlease endif ifeq ($(uname_S),AIX) NO_STRCASESTR=YesPlease NO_MEMMEM = YesPlease NO_MKDTEMP = YesPlease + NO_MKSTEMPS = YesPlease NO_STRLCPY = YesPlease NO_NSEC = YesPlease FREAD_READS_DIRECTORIES = UnfortunatelyYes @@ -786,12 +820,14 @@ endif ifeq ($(uname_S),GNU) # GNU/Hurd NO_STRLCPY=YesPlease + NO_MKSTEMPS = YesPlease endif ifeq ($(uname_S),IRIX64) NO_IPV6=YesPlease NO_SETENV=YesPlease NO_STRCASESTR=YesPlease NO_MEMMEM = YesPlease + NO_MKSTEMPS = YesPlease NO_STRLCPY = YesPlease NO_SOCKADDR_STORAGE=YesPlease SHELL_PATH=/usr/gnu/bin/bash @@ -804,6 +840,7 @@ ifeq ($(uname_S),HP-UX) NO_SETENV=YesPlease NO_STRCASESTR=YesPlease NO_MEMMEM = YesPlease + NO_MKSTEMPS = YesPlease NO_STRLCPY = YesPlease NO_MKDTEMP = YesPlease NO_UNSETENV = YesPlease @@ -816,9 +853,11 @@ ifneq (,$(findstring CYGWIN,$(uname_S))) UNRELIABLE_FSTAT = UnfortunatelyYes endif ifneq (,$(findstring MINGW,$(uname_S))) + pathsep = ; NO_PREAD = YesPlease NO_OPENSSL = YesPlease NO_CURL = YesPlease + NO_LIBGEN_H = YesPlease NO_SYMLINK_HEAD = YesPlease NO_IPV6 = YesPlease NO_SETENV = YesPlease @@ -832,6 +871,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) NO_C99_FORMAT = YesPlease NO_STRTOUMAX = YesPlease NO_MKDTEMP = YesPlease + NO_MKSTEMPS = YesPlease SNPRINTF_RETURNS_BOGUS = YesPlease NO_SVN_TESTS = YesPlease NO_PERL_MAKEMAKER = YesPlease @@ -851,11 +891,20 @@ ifneq (,$(findstring MINGW,$(uname_S))) endif ifneq (,$(findstring arm,$(uname_M))) ARM_SHA1 = YesPlease + NO_MKSTEMPS = YesPlease endif -include config.mak.autogen -include config.mak +ifdef SANE_TOOL_PATH +SANE_TOOL_PATH_SQ = $(subst ','\'',$(SANE_TOOL_PATH)) +BROKEN_PATH_FIX = 's|^\# @@BROKEN_PATH_FIX@@$$|git_broken_path_fix $(SANE_TOOL_PATH_SQ)|' +PATH := $(SANE_TOOL_PATH):${PATH} +else +BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d' +endif + ifeq ($(uname_S),Darwin) ifndef NO_FINK ifeq ($(shell test -d /sw/lib && echo y),y) @@ -882,6 +931,11 @@ ifndef CC_LD_DYNPATH endif endif +ifdef NO_LIBGEN_H + COMPAT_CFLAGS += -DNO_LIBGEN_H + COMPAT_OBJS += compat/basename.o +endif + ifdef NO_CURL BASIC_CFLAGS += -DNO_CURL else @@ -954,6 +1008,9 @@ endif ifdef NEEDS_NSL EXTLIBS += -lnsl endif +ifdef NEEDS_RESOLV + EXTLIBS += -lresolv +endif ifdef NO_D_TYPE_IN_DIRENT BASIC_CFLAGS += -DNO_D_TYPE_IN_DIRENT endif @@ -1009,6 +1066,10 @@ ifdef NO_MKDTEMP COMPAT_CFLAGS += -DNO_MKDTEMP COMPAT_OBJS += compat/mkdtemp.o endif +ifdef NO_MKSTEMPS + COMPAT_CFLAGS += -DNO_MKSTEMPS + COMPAT_OBJS += compat/mkstemps.o +endif ifdef NO_UNSETENV COMPAT_CFLAGS += -DNO_UNSETENV COMPAT_OBJS += compat/unsetenv.o @@ -1252,9 +1313,9 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh $(QUIET_GEN)$(RM) $@ $@+ && \ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \ - -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ + -e $(BROKEN_PATH_FIX) \ $@.sh >$@+ && \ chmod +x $@+ && \ mv $@+ $@ @@ -1271,7 +1332,7 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl sed -e '1{' \ -e ' s|#!.*perl|#!$(PERL_PATH_SQ)|' \ -e ' h' \ - -e ' s=.*=use lib (split(/:/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \ + -e ' s=.*=use lib (split(/$(pathsep)/, $$ENV{GITPERLLIB} || "@@INSTLIBDIR@@"));=' \ -e ' H' \ -e ' x' \ -e '}' \ @@ -1494,6 +1555,8 @@ test-delta$X: diff-delta.o patch-delta.o test-parse-options$X: parse-options.o +test-parse-options.o: parse-options.h + .PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS)) test-%$X: test-%.o $(GITLIBS) @@ -1 +1 @@ -Documentation/RelNotes-1.6.3.2.txt
\ No newline at end of file +Documentation/RelNotes-1.6.4.txt
\ No newline at end of file @@ -309,7 +309,7 @@ static int parse_archive_args(int argc, const char **argv, OPT_END() }; - argc = parse_options(argc, argv, opts, archive_usage, 0); + argc = parse_options(argc, argv, NULL, opts, archive_usage, 0); if (remote) die("Unexpected option --remote"); @@ -6,15 +6,30 @@ #include "list-objects.h" #include "quote.h" #include "sha1-lookup.h" +#include "run-command.h" #include "bisect.h" -static unsigned char (*skipped_sha1)[20]; -static int skipped_sha1_nr; -static int skipped_sha1_alloc; +struct sha1_array { + unsigned char (*sha1)[20]; + int sha1_nr; + int sha1_alloc; + int sorted; +}; + +static struct sha1_array good_revs; +static struct sha1_array skipped_revs; + +static const unsigned char *current_bad_sha1; + +struct argv_array { + const char **argv; + int argv_nr; + int argv_alloc; +}; -static const char **rev_argv; -static int rev_argv_nr; -static int rev_argv_alloc; +static const char *argv_diff_tree[] = {"diff-tree", "--pretty", NULL, NULL}; +static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL}; +static const char *argv_show_branch[] = {"show-branch", NULL, NULL}; /* bits #0-15 in revision.h */ @@ -398,23 +413,37 @@ struct commit_list *find_bisection(struct commit_list *list, return best; } +static void argv_array_push(struct argv_array *array, const char *string) +{ + ALLOC_GROW(array->argv, array->argv_nr + 1, array->argv_alloc); + array->argv[array->argv_nr++] = string; +} + +static void argv_array_push_sha1(struct argv_array *array, + const unsigned char *sha1, + const char *format) +{ + struct strbuf buf = STRBUF_INIT; + strbuf_addf(&buf, format, sha1_to_hex(sha1)); + argv_array_push(array, strbuf_detach(&buf, NULL)); +} + +static void sha1_array_push(struct sha1_array *array, + const unsigned char *sha1) +{ + ALLOC_GROW(array->sha1, array->sha1_nr + 1, array->sha1_alloc); + hashcpy(array->sha1[array->sha1_nr++], sha1); +} + static int register_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { if (!strcmp(refname, "bad")) { - ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); - rev_argv[rev_argv_nr++] = xstrdup(sha1_to_hex(sha1)); + current_bad_sha1 = sha1; } else if (!prefixcmp(refname, "good-")) { - const char *hex = sha1_to_hex(sha1); - char *good = xmalloc(strlen(hex) + 2); - *good = '^'; - memcpy(good + 1, hex, strlen(hex) + 1); - ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); - rev_argv[rev_argv_nr++] = good; + sha1_array_push(&good_revs, sha1); } else if (!prefixcmp(refname, "skip-")) { - ALLOC_GROW(skipped_sha1, skipped_sha1_nr + 1, - skipped_sha1_alloc); - hashcpy(skipped_sha1[skipped_sha1_nr++], sha1); + sha1_array_push(&skipped_revs, sha1); } return 0; @@ -425,7 +454,7 @@ static int read_bisect_refs(void) return for_each_ref_in("refs/bisect/", register_ref, NULL); } -void read_bisect_paths(void) +void read_bisect_paths(struct argv_array *array) { struct strbuf str = STRBUF_INIT; const char *filename = git_path("BISECT_NAMES"); @@ -440,8 +469,8 @@ void read_bisect_paths(void) strbuf_trim(&str); quoted = strbuf_detach(&str, NULL); - res = sq_dequote_to_argv(quoted, &rev_argv, - &rev_argv_nr, &rev_argv_alloc); + res = sq_dequote_to_argv(quoted, &array->argv, + &array->argv_nr, &array->argv_alloc); if (res) die("Badly quoted content in file '%s': %s", filename, quoted); @@ -451,26 +480,45 @@ void read_bisect_paths(void) fclose(fp); } -static int skipcmp(const void *a, const void *b) +static int array_cmp(const void *a, const void *b) { return hashcmp(a, b); } -static void prepare_skipped(void) +static void sort_sha1_array(struct sha1_array *array) { - qsort(skipped_sha1, skipped_sha1_nr, sizeof(*skipped_sha1), skipcmp); + qsort(array->sha1, array->sha1_nr, sizeof(*array->sha1), array_cmp); + + array->sorted = 1; } -static const unsigned char *skipped_sha1_access(size_t index, void *table) +static const unsigned char *sha1_access(size_t index, void *table) { - unsigned char (*skipped)[20] = table; - return skipped[index]; + unsigned char (*array)[20] = table; + return array[index]; } -static int lookup_skipped(unsigned char *sha1) +static int lookup_sha1_array(struct sha1_array *array, + const unsigned char *sha1) { - return sha1_pos(sha1, skipped_sha1, skipped_sha1_nr, - skipped_sha1_access); + if (!array->sorted) + sort_sha1_array(array); + + return sha1_pos(sha1, array->sha1, array->sha1_nr, sha1_access); +} + +static char *join_sha1_array_hex(struct sha1_array *array, char delim) +{ + struct strbuf joined_hexs = STRBUF_INIT; + int i; + + for (i = 0; i < array->sha1_nr; i++) { + strbuf_addstr(&joined_hexs, sha1_to_hex(array->sha1[i])); + if (i + 1 < array->sha1_nr) + strbuf_addch(&joined_hexs, delim); + } + + return strbuf_detach(&joined_hexs, NULL); } struct commit_list *filter_skipped(struct commit_list *list, @@ -481,15 +529,14 @@ struct commit_list *filter_skipped(struct commit_list *list, *tried = NULL; - if (!skipped_sha1_nr) + if (!skipped_revs.sha1_nr) return list; - prepare_skipped(); - while (list) { struct commit_list *next = list->next; list->next = NULL; - if (0 <= lookup_skipped(list->item->object.sha1)) { + if (0 <= lookup_sha1_array(&skipped_revs, + list->item->object.sha1)) { /* Move current to tried list */ *tried = list; tried = &list->next; @@ -506,51 +553,323 @@ struct commit_list *filter_skipped(struct commit_list *list, return filtered; } -static void bisect_rev_setup(struct rev_info *revs, const char *prefix) +static void bisect_rev_setup(struct rev_info *revs, const char *prefix, + const char *bad_format, const char *good_format, + int read_paths) { + struct argv_array rev_argv = { NULL, 0, 0 }; + int i; + init_revisions(revs, prefix); revs->abbrev = 0; revs->commit_format = CMIT_FMT_UNSPECIFIED; - /* argv[0] will be ignored by setup_revisions */ - ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); - rev_argv[rev_argv_nr++] = xstrdup("bisect_rev_setup"); + /* rev_argv.argv[0] will be ignored by setup_revisions */ + argv_array_push(&rev_argv, xstrdup("bisect_rev_setup")); + argv_array_push_sha1(&rev_argv, current_bad_sha1, bad_format); + for (i = 0; i < good_revs.sha1_nr; i++) + argv_array_push_sha1(&rev_argv, good_revs.sha1[i], + good_format); + argv_array_push(&rev_argv, xstrdup("--")); + if (read_paths) + read_bisect_paths(&rev_argv); + argv_array_push(&rev_argv, NULL); + + setup_revisions(rev_argv.argv_nr, rev_argv.argv, revs, NULL); +} - if (read_bisect_refs()) - die("reading bisect refs failed"); +static void bisect_common(struct rev_info *revs) +{ + if (prepare_revision_walk(revs)) + die("revision walk setup failed"); + if (revs->tree_objects) + mark_edges_uninteresting(revs->commits, revs, NULL); +} + +static void exit_if_skipped_commits(struct commit_list *tried, + const unsigned char *bad) +{ + if (!tried) + return; + + printf("There are only 'skip'ped commits left to test.\n" + "The first bad commit could be any of:\n"); + print_commit_list(tried, "%s\n", "%s\n"); + if (bad) + printf("%s\n", sha1_to_hex(bad)); + printf("We cannot bisect more!\n"); + exit(2); +} + +static int is_expected_rev(const unsigned char *sha1) +{ + const char *filename = git_path("BISECT_EXPECTED_REV"); + struct stat st; + struct strbuf str = STRBUF_INIT; + FILE *fp; + int res = 0; + + if (stat(filename, &st) || !S_ISREG(st.st_mode)) + return 0; + + fp = fopen(filename, "r"); + if (!fp) + return 0; + + if (strbuf_getline(&str, fp, '\n') != EOF) + res = !strcmp(str.buf, sha1_to_hex(sha1)); + + strbuf_release(&str); + fclose(fp); + + return res; +} + +static void mark_expected_rev(char *bisect_rev_hex) +{ + int len = strlen(bisect_rev_hex); + const char *filename = git_path("BISECT_EXPECTED_REV"); + int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); + + if (fd < 0) + die("could not create file '%s': %s", + filename, strerror(errno)); - ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); - rev_argv[rev_argv_nr++] = xstrdup("--"); + bisect_rev_hex[len] = '\n'; + write_or_die(fd, bisect_rev_hex, len + 1); + bisect_rev_hex[len] = '\0'; - read_bisect_paths(); + if (close(fd) < 0) + die("closing file %s: %s", filename, strerror(errno)); +} + +static int bisect_checkout(char *bisect_rev_hex) +{ + int res; - ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc); - rev_argv[rev_argv_nr++] = NULL; + mark_expected_rev(bisect_rev_hex); - setup_revisions(rev_argv_nr, rev_argv, revs, NULL); + argv_checkout[2] = bisect_rev_hex; + res = run_command_v_opt(argv_checkout, RUN_GIT_CMD); + if (res) + exit(res); - revs->limited = 1; + argv_show_branch[1] = bisect_rev_hex; + return run_command_v_opt(argv_show_branch, RUN_GIT_CMD); } -int bisect_next_vars(const char *prefix) +static struct commit *get_commit_reference(const unsigned char *sha1) +{ + struct commit *r = lookup_commit_reference(sha1); + if (!r) + die("Not a valid commit name %s", sha1_to_hex(sha1)); + return r; +} + +static struct commit **get_bad_and_good_commits(int *rev_nr) +{ + int len = 1 + good_revs.sha1_nr; + struct commit **rev = xmalloc(len * sizeof(*rev)); + int i, n = 0; + + rev[n++] = get_commit_reference(current_bad_sha1); + for (i = 0; i < good_revs.sha1_nr; i++) + rev[n++] = get_commit_reference(good_revs.sha1[i]); + *rev_nr = n; + + return rev; +} + +static void handle_bad_merge_base(void) +{ + if (is_expected_rev(current_bad_sha1)) { + char *bad_hex = sha1_to_hex(current_bad_sha1); + char *good_hex = join_sha1_array_hex(&good_revs, ' '); + + fprintf(stderr, "The merge base %s is bad.\n" + "This means the bug has been fixed " + "between %s and [%s].\n", + bad_hex, bad_hex, good_hex); + + exit(3); + } + + fprintf(stderr, "Some good revs are not ancestor of the bad rev.\n" + "git bisect cannot work properly in this case.\n" + "Maybe you mistake good and bad revs?\n"); + exit(1); +} + +void handle_skipped_merge_base(const unsigned char *mb) +{ + char *mb_hex = sha1_to_hex(mb); + char *bad_hex = sha1_to_hex(current_bad_sha1); + char *good_hex = join_sha1_array_hex(&good_revs, ' '); + + fprintf(stderr, "Warning: the merge base between %s and [%s] " + "must be skipped.\n" + "So we cannot be sure the first bad commit is " + "between %s and %s.\n" + "We continue anyway.\n", + bad_hex, good_hex, mb_hex, bad_hex); + free(good_hex); +} + +/* + * "check_merge_bases" checks that merge bases are not "bad". + * + * - If one is "bad", it means the user assumed something wrong + * and we must exit with a non 0 error code. + * - If one is "good", that's good, we have nothing to do. + * - If one is "skipped", we can't know but we should warn. + * - If we don't know, we should check it out and ask the user to test. + */ +static void check_merge_bases(void) +{ + struct commit_list *result; + int rev_nr; + struct commit **rev = get_bad_and_good_commits(&rev_nr); + + result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0); + + for (; result; result = result->next) { + const unsigned char *mb = result->item->object.sha1; + if (!hashcmp(mb, current_bad_sha1)) { + handle_bad_merge_base(); + } else if (0 <= lookup_sha1_array(&good_revs, mb)) { + continue; + } else if (0 <= lookup_sha1_array(&skipped_revs, mb)) { + handle_skipped_merge_base(mb); + } else { + printf("Bisecting: a merge base must be tested\n"); + exit(bisect_checkout(sha1_to_hex(mb))); + } + } + + free(rev); + free_commit_list(result); +} + +static int check_ancestors(const char *prefix) { struct rev_info revs; - struct rev_list_info info; - int reaches = 0, all = 0; + struct object_array pending_copy; + int i, res; - memset(&info, 0, sizeof(info)); - info.revs = &revs; - info.bisect_show_flags = BISECT_SHOW_TRIED | BISECT_SHOW_STRINGED; + bisect_rev_setup(&revs, prefix, "^%s", "%s", 0); - bisect_rev_setup(&revs, prefix); + /* Save pending objects, so they can be cleaned up later. */ + memset(&pending_copy, 0, sizeof(pending_copy)); + for (i = 0; i < revs.pending.nr; i++) + add_object_array(revs.pending.objects[i].item, + revs.pending.objects[i].name, + &pending_copy); - if (prepare_revision_walk(&revs)) - die("revision walk setup failed"); - if (revs.tree_objects) - mark_edges_uninteresting(revs.commits, &revs, NULL); + bisect_common(&revs); + res = (revs.commits != NULL); + + /* Clean up objects used, as they will be reused. */ + for (i = 0; i < pending_copy.nr; i++) { + struct object *o = pending_copy.objects[i].item; + unparse_commit((struct commit *)o); + } + + return res; +} + +/* + * "check_good_are_ancestors_of_bad" checks that all "good" revs are + * ancestor of the "bad" rev. + * + * If that's not the case, we need to check the merge bases. + * If a merge base must be tested by the user, its source code will be + * checked out to be tested by the user and we will exit. + */ +static void check_good_are_ancestors_of_bad(const char *prefix) +{ + const char *filename = git_path("BISECT_ANCESTORS_OK"); + struct stat st; + int fd; + + if (!current_bad_sha1) + die("a bad revision is needed"); + + /* Check if file BISECT_ANCESTORS_OK exists. */ + if (!stat(filename, &st) && S_ISREG(st.st_mode)) + return; + + /* Bisecting with no good rev is ok. */ + if (good_revs.sha1_nr == 0) + return; + + /* Check if all good revs are ancestor of the bad rev. */ + if (check_ancestors(prefix)) + check_merge_bases(); + + /* Create file BISECT_ANCESTORS_OK. */ + fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600); + if (fd < 0) + warning("could not create file '%s': %s", + filename, strerror(errno)); + else + close(fd); +} + +/* + * We use the convention that exiting with an exit code 10 means that + * the bisection process finished successfully. + * In this case the calling shell script should exit 0. + */ +int bisect_next_all(const char *prefix) +{ + struct rev_info revs; + struct commit_list *tried; + int reaches = 0, all = 0, nr; + const unsigned char *bisect_rev; + char bisect_rev_hex[41]; + + if (read_bisect_refs()) + die("reading bisect refs failed"); + + check_good_are_ancestors_of_bad(prefix); + + bisect_rev_setup(&revs, prefix, "%s", "^%s", 1); + revs.limited = 1; + + bisect_common(&revs); revs.commits = find_bisection(revs.commits, &reaches, &all, - !!skipped_sha1_nr); + !!skipped_revs.sha1_nr); + revs.commits = filter_skipped(revs.commits, &tried, 0); + + if (!revs.commits) { + /* + * We should exit here only if the "bad" + * commit is also a "skip" commit. + */ + exit_if_skipped_commits(tried, NULL); + + printf("%s was both good and bad\n", + sha1_to_hex(current_bad_sha1)); + exit(1); + } - return show_bisect_vars(&info, reaches, all); + bisect_rev = revs.commits->item->object.sha1; + memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), 41); + + if (!hashcmp(bisect_rev, current_bad_sha1)) { + exit_if_skipped_commits(tried, current_bad_sha1); + printf("%s is first bad commit\n", bisect_rev_hex); + argv_diff_tree[2] = bisect_rev_hex; + run_command_v_opt(argv_diff_tree, RUN_GIT_CMD); + /* This means the bisection process succeeded. */ + exit(10); + } + + nr = all - reaches - 1; + printf("Bisecting: %d revisions left to test after this " + "(roughly %d steps)\n", nr, estimate_bisect_steps(all)); + + return bisect_checkout(bisect_rev_hex); } + @@ -9,10 +9,13 @@ extern struct commit_list *filter_skipped(struct commit_list *list, struct commit_list **tried, int show_all); +extern void print_commit_list(struct commit_list *list, + const char *format_cur, + const char *format_last); + /* bisect_show_flags flags in struct rev_list_info */ #define BISECT_SHOW_ALL (1<<0) #define BISECT_SHOW_TRIED (1<<1) -#define BISECT_SHOW_STRINGED (1<<2) struct rev_list_info { struct rev_info *revs; @@ -24,6 +27,8 @@ struct rev_list_info { extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all); -extern int bisect_next_vars(const char *prefix); +extern int bisect_next_all(const char *prefix); + +extern int estimate_bisect_steps(int all); #endif diff --git a/builtin-add.c b/builtin-add.c index ad889aac5b..c1b229a9d8 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -10,12 +10,14 @@ #include "cache-tree.h" #include "run-command.h" #include "parse-options.h" +#include "diff.h" +#include "revision.h" static const char * const builtin_add_usage[] = { "git add [options] [--] <filepattern>...", NULL }; -static int patch_interactive, add_interactive; +static int patch_interactive, add_interactive, edit_interactive; static int take_worktree_changes; static void fill_pathspec_matches(const char **pathspec, char *seen, int specs) @@ -187,6 +189,51 @@ int interactive_add(int argc, const char **argv, const char *prefix) return status; } +int edit_patch(int argc, const char **argv, const char *prefix) +{ + char *file = xstrdup(git_path("ADD_EDIT.patch")); + const char *apply_argv[] = { "apply", "--recount", "--cached", + file, NULL }; + struct child_process child; + struct rev_info rev; + int out; + struct stat st; + + git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ + + if (read_cache() < 0) + die ("Could not read the index"); + + init_revisions(&rev, prefix); + rev.diffopt.context = 7; + + argc = setup_revisions(argc, argv, &rev, NULL); + rev.diffopt.output_format = DIFF_FORMAT_PATCH; + out = open(file, O_CREAT | O_WRONLY, 0644); + if (out < 0) + die ("Could not open '%s' for writing.", file); + rev.diffopt.file = fdopen(out, "w"); + rev.diffopt.close_file = 1; + if (run_diff_files(&rev, 0)) + die ("Could not write patch"); + + launch_editor(file, NULL, NULL); + + if (stat(file, &st)) + die("Could not stat '%s'", file); + if (!st.st_size) + die("Empty patch. Aborted."); + + memset(&child, 0, sizeof(child)); + child.git_cmd = 1; + child.argv = apply_argv; + if (run_command(&child)) + die ("Could not apply '%s'", file); + + unlink(file); + return 0; +} + static struct lock_file lock_file; static const char ignore_error[] = @@ -201,6 +248,7 @@ static struct option builtin_add_options[] = { OPT_GROUP(""), OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"), OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"), + OPT_BOOLEAN('e', "edit", &edit_interactive, "edit current diff and apply"), OPT_BOOLEAN('f', "force", &ignored_too, "allow adding otherwise ignored files"), OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"), OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"), @@ -250,15 +298,20 @@ int cmd_add(int argc, const char **argv, const char *prefix) int add_new_files; int require_pathspec; - argc = parse_options(argc, argv, builtin_add_options, - builtin_add_usage, 0); + argc = parse_options(argc, argv, prefix, builtin_add_options, + builtin_add_usage, PARSE_OPT_KEEP_ARGV0); if (patch_interactive) add_interactive = 1; if (add_interactive) - exit(interactive_add(argc, argv, prefix)); + exit(interactive_add(argc - 1, argv + 1, prefix)); git_config(add_config, NULL); + if (edit_interactive) + return(edit_patch(argc, argv, prefix)); + argc--; + argv++; + if (addremove && take_worktree_changes) die("-A and -u are mutually incompatible"); if ((addremove || take_worktree_changes) && !argc) { diff --git a/builtin-apply.c b/builtin-apply.c index 8a3771e87e..94ba2bdd5b 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -320,6 +320,20 @@ static int name_terminate(const char *name, int namelen, int c, int terminate) return 1; } +/* remove double slashes to make --index work with such filenames */ +static char *squash_slash(char *name) +{ + int i = 0, j = 0; + + while (name[i]) { + if ((name[j++] = name[i++]) == '/') + while (name[i] == '/') + i++; + } + name[j] = '\0'; + return name; +} + static char *find_name(const char *line, char *def, int p_value, int terminate) { int len; @@ -349,7 +363,7 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) free(def); if (root) strbuf_insert(&name, 0, root, root_len); - return strbuf_detach(&name, NULL); + return squash_slash(strbuf_detach(&name, NULL)); } } strbuf_release(&name); @@ -369,10 +383,10 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) start = line; } if (!start) - return def; + return squash_slash(def); len = line - start; if (!len) - return def; + return squash_slash(def); /* * Generally we prefer the shorter name, especially @@ -383,7 +397,7 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) if (def) { int deflen = strlen(def); if (deflen < len && !strncmp(start, def, deflen)) - return def; + return squash_slash(def); free(def); } @@ -392,10 +406,10 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) strcpy(ret, root); memcpy(ret + root_len, start, len); ret[root_len + len] = '\0'; - return ret; + return squash_slash(ret); } - return xmemdupz(start, len); + return squash_slash(xmemdupz(start, len)); } static int count_slashes(const char *cp) @@ -3278,7 +3292,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) "apply a patch without touching the working tree"), OPT_BOOLEAN(0, "apply", &force_apply, "also apply the patch (use with --stat/--summary/--check)"), - OPT_STRING(0, "build-fake-ancestor", &fake_ancestor, "file", + OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor, "build a temporary index based on embedded index information"), { OPTION_CALLBACK, 'z', NULL, NULL, NULL, "paths are separated with NUL character", @@ -3313,8 +3327,9 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) if (apply_default_whitespace) parse_whitespace_option(apply_default_whitespace); - argc = parse_options(argc, argv, builtin_apply_options, + argc = parse_options(argc, argv, prefix, builtin_apply_options, apply_usage, 0); + if (apply_with_reject) apply = apply_verbosely = 1; if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor)) diff --git a/builtin-archive.c b/builtin-archive.c index ab50cebba0..3c5a5a7822 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -80,7 +80,8 @@ int cmd_archive(int argc, const char **argv, const char *prefix) OPT_END() }; - argc = parse_options(argc, argv, local_opts, NULL, PARSE_OPT_KEEP_ALL); + argc = parse_options(argc, argv, prefix, local_opts, NULL, + PARSE_OPT_KEEP_ALL); if (output) create_output_file(output); diff --git a/builtin-bisect--helper.c b/builtin-bisect--helper.c index 8fe778766a..5b226399e1 100644 --- a/builtin-bisect--helper.c +++ b/builtin-bisect--helper.c @@ -4,24 +4,25 @@ #include "bisect.h" static const char * const git_bisect_helper_usage[] = { - "git bisect--helper --next-vars", + "git bisect--helper --next-all", NULL }; int cmd_bisect__helper(int argc, const char **argv, const char *prefix) { - int next_vars = 0; + int next_all = 0; struct option options[] = { - OPT_BOOLEAN(0, "next-vars", &next_vars, - "output next bisect step variables"), + OPT_BOOLEAN(0, "next-all", &next_all, + "perform 'git bisect next'"), OPT_END() }; - argc = parse_options(argc, argv, options, git_bisect_helper_usage, 0); + argc = parse_options(argc, argv, prefix, options, + git_bisect_helper_usage, 0); - if (!next_vars) + if (!next_all) usage_with_options(git_bisect_helper_usage, options); - /* next-vars */ - return bisect_next_vars(prefix); + /* next-all */ + return bisect_next_all(prefix); } diff --git a/builtin-blame.c b/builtin-blame.c index cf74a92614..0c2d29a430 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -362,18 +362,28 @@ static struct origin *find_origin(struct scoreboard *sb, "", &diff_opts); diffcore_std(&diff_opts); - /* It is either one entry that says "modified", or "created", - * or nothing. - */ if (!diff_queued_diff.nr) { /* The path is the same as parent */ porigin = get_origin(sb, parent, origin->path); hashcpy(porigin->blob_sha1, origin->blob_sha1); - } - else if (diff_queued_diff.nr != 1) - die("internal error in blame::find_origin"); - else { - struct diff_filepair *p = diff_queued_diff.queue[0]; + } else { + /* + * Since origin->path is a pathspec, if the parent + * commit had it as a directory, we will see a whole + * bunch of deletion of files in the directory that we + * do not care about. + */ + int i; + struct diff_filepair *p = NULL; + for (i = 0; i < diff_queued_diff.nr; i++) { + const char *name; + p = diff_queued_diff.queue[i]; + name = p->one->path ? p->one->path : p->two->path; + if (!strcmp(name, origin->path)) + break; + } + if (!p) + die("internal error in blame::find_origin"); switch (p->status) { default: die("internal error in blame::find_origin (%c)", @@ -2229,7 +2239,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) save_commit_buffer = 0; dashdash_pos = 0; - parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH | + parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0); for (;;) { switch (parse_options_step(&ctx, options, blame_opt_usage)) { diff --git a/builtin-branch.c b/builtin-branch.c index 91098ca9b1..5687d6042c 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -547,7 +547,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) struct option options[] = { OPT_GROUP("Generic options"), OPT__VERBOSE(&verbose), - OPT_SET_INT( 0 , "track", &track, "set up tracking mode (see git-pull(1))", + OPT_SET_INT('t', "track", &track, "set up tracking mode (see git-pull(1))", BRANCH_TRACK_EXPLICIT), OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"), OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches", @@ -610,7 +610,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix) } hashcpy(merge_filter_ref, head_sha1); - argc = parse_options(argc, argv, options, builtin_branch_usage, 0); + argc = parse_options(argc, argv, prefix, options, builtin_branch_usage, + 0); if (!!delete + !!rename + !!force_create > 1) usage_with_options(builtin_branch_usage, options); diff --git a/builtin-cat-file.c b/builtin-cat-file.c index 43ffe7ffae..5906842008 100644 --- a/builtin-cat-file.c +++ b/builtin-cat-file.c @@ -231,7 +231,7 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) if (argc != 3 && argc != 2) usage_with_options(cat_file_usage, options); - argc = parse_options(argc, argv, options, cat_file_usage, 0); + argc = parse_options(argc, argv, prefix, options, cat_file_usage, 0); if (opt) { if (argc == 1) diff --git a/builtin-check-attr.c b/builtin-check-attr.c index 15a04b7179..8bd0430098 100644 --- a/builtin-check-attr.c +++ b/builtin-check-attr.c @@ -69,8 +69,8 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix) int cnt, i, doubledash; const char *errstr = NULL; - argc = parse_options(argc, argv, check_attr_options, check_attr_usage, - PARSE_OPT_KEEP_DASHDASH); + argc = parse_options(argc, argv, prefix, check_attr_options, + check_attr_usage, PARSE_OPT_KEEP_DASHDASH); if (!argc) usage_with_options(check_attr_usage, check_attr_options); diff --git a/builtin-checkout-index.c b/builtin-checkout-index.c index afe35e246c..a7a5ee10f3 100644 --- a/builtin-checkout-index.c +++ b/builtin-checkout-index.c @@ -249,7 +249,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) die("invalid cache"); } - argc = parse_options(argc, argv, builtin_checkout_index_options, + argc = parse_options(argc, argv, prefix, builtin_checkout_index_options, builtin_checkout_index_usage, 0); state.force = force; state.quiet = quiet; diff --git a/builtin-checkout.c b/builtin-checkout.c index b8a4b0139b..8a9a474218 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -605,7 +605,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.track = BRANCH_TRACK_UNSPECIFIED; - argc = parse_options(argc, argv, options, checkout_usage, + argc = parse_options(argc, argv, prefix, options, checkout_usage, PARSE_OPT_KEEP_DASHDASH); /* --track without -b should DWIM */ diff --git a/builtin-clean.c b/builtin-clean.c index c5ad33d3e6..1c1b6d26e9 100644 --- a/builtin-clean.c +++ b/builtin-clean.c @@ -56,7 +56,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix) else config_set = 1; - argc = parse_options(argc, argv, options, builtin_clean_usage, 0); + argc = parse_options(argc, argv, prefix, options, builtin_clean_usage, + 0); memset(&dir, 0, sizeof(dir)); if (ignored_only) diff --git a/builtin-clone.c b/builtin-clone.c index ba286e0160..5c46496a43 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -104,11 +104,12 @@ static char *get_repo_path(const char *repo, int *is_bundle) static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) { const char *end = repo + strlen(repo), *start; + char *dir; /* - * Strip trailing slashes and /.git + * Strip trailing spaces, slashes and /.git */ - while (repo < end && is_dir_sep(end[-1])) + while (repo < end && (is_dir_sep(end[-1]) || isspace(end[-1]))) end--; if (end - repo > 5 && is_dir_sep(end[-5]) && !strncmp(end - 4, ".git", 4)) { @@ -140,10 +141,33 @@ static char *guess_dir_name(const char *repo, int is_bundle, int is_bare) if (is_bare) { struct strbuf result = STRBUF_INIT; strbuf_addf(&result, "%.*s.git", (int)(end - start), start); - return strbuf_detach(&result, 0); + dir = strbuf_detach(&result, 0); + } else + dir = xstrndup(start, end - start); + /* + * Replace sequences of 'control' characters and whitespace + * with one ascii space, remove leading and trailing spaces. + */ + if (*dir) { + char *out = dir; + int prev_space = 1 /* strip leading whitespace */; + for (end = dir; *end; ++end) { + char ch = *end; + if ((unsigned char)ch < '\x20') + ch = '\x20'; + if (isspace(ch)) { + if (prev_space) + continue; + prev_space = 1; + } else + prev_space = 0; + *out++ = ch; + } + *out = '\0'; + if (out > dir && prev_space) + out[-1] = '\0'; } - - return xstrndup(start, end - start); + return dir; } static void strip_trailing_slashes(char *dir) @@ -336,7 +360,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) junk_pid = getpid(); - argc = parse_options(argc, argv, builtin_clone_options, + argc = parse_options(argc, argv, prefix, builtin_clone_options, builtin_clone_usage, 0); if (argc == 0) diff --git a/builtin-commit.c b/builtin-commit.c index 81371b1d26..41e222d267 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -88,13 +88,13 @@ static struct option builtin_commit_options[] = { OPT__VERBOSE(&verbose), OPT_GROUP("Commit message options"), - OPT_STRING('F', "file", &logfile, "FILE", "read log from file"), + OPT_FILENAME('F', "file", &logfile, "read log from file"), OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"), OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m), OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "), OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"), OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"), - OPT_STRING('t', "template", &template_file, "FILE", "use specified template file"), + OPT_FILENAME('t', "template", &template_file, "use specified template file"), OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"), OPT_GROUP("Commit contents options"), @@ -697,9 +697,8 @@ static int parse_and_validate_options(int argc, const char *argv[], { int f = 0; - argc = parse_options(argc, argv, builtin_commit_options, usage, 0); - logfile = parse_options_fix_filename(prefix, logfile); - template_file = parse_options_fix_filename(prefix, template_file); + argc = parse_options(argc, argv, prefix, builtin_commit_options, usage, + 0); if (force_author && !strchr(force_author, '>')) force_author = find_author_by_nickname(force_author); diff --git a/builtin-config.c b/builtin-config.c index a81bc8bbf0..60915f91ca 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -316,7 +316,8 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix) config_exclusive_filename = getenv(CONFIG_ENVIRONMENT); - argc = parse_options(argc, argv, builtin_config_options, builtin_config_usage, + argc = parse_options(argc, argv, prefix, builtin_config_options, + builtin_config_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (use_global_config + use_system_config + !!given_config_file > 1) { diff --git a/builtin-count-objects.c b/builtin-count-objects.c index b814fe5070..1b0b6c84ea 100644 --- a/builtin-count-objects.c +++ b/builtin-count-objects.c @@ -83,7 +83,7 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix) OPT_END(), }; - argc = parse_options(argc, argv, opts, count_objects_usage, 0); + argc = parse_options(argc, argv, prefix, opts, count_objects_usage, 0); /* we do not take arguments other than flags for now */ if (argc) usage_with_options(count_objects_usage, opts); diff --git a/builtin-describe.c b/builtin-describe.c index 63c6a19da5..7a662980d1 100644 --- a/builtin-describe.c +++ b/builtin-describe.c @@ -322,7 +322,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix) OPT_END(), }; - argc = parse_options(argc, argv, options, describe_usage, 0); + argc = parse_options(argc, argv, prefix, options, describe_usage, 0); if (max_candidates < 0) max_candidates = 0; else if (max_candidates > MAX_TAGS) diff --git a/builtin-fast-export.c b/builtin-fast-export.c index 6731713223..6cef810312 100644 --- a/builtin-fast-export.c +++ b/builtin-fast-export.c @@ -515,7 +515,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) init_revisions(&revs, prefix); argc = setup_revisions(argc, argv, &revs, NULL); - argc = parse_options(argc, argv, options, fast_export_usage, 0); + argc = parse_options(argc, argv, prefix, options, fast_export_usage, 0); if (argc > 1) usage_with_options (fast_export_usage, options); diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 6202462216..629735f547 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -483,7 +483,9 @@ static int sideband_demux(int fd, void *data) { int *xd = data; - return recv_sideband("fetch-pack", xd[0], fd); + int ret = recv_sideband("fetch-pack", xd[0], fd); + close(fd); + return ret; } static int get_pack(int xd[2], char **pack_lockfile) diff --git a/builtin-fetch.c b/builtin-fetch.c index 1f7a3f1ce6..cd5eb9aff5 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -202,7 +202,7 @@ static int update_local_ref(struct ref *ref, struct commit *current = NULL, *updated; enum object_type type; struct branch *current_branch = branch_get(NULL); - const char *pretty_ref = prettify_ref(ref); + const char *pretty_ref = prettify_refname(ref->name); *display = 0; type = sha1_object_info(ref->new_sha1, NULL); @@ -294,7 +294,7 @@ static int update_local_ref(struct ref *ref, } } -static int store_updated_refs(const char *url, const char *remote_name, +static int store_updated_refs(const char *raw_url, const char *remote_name, struct ref *ref_map) { FILE *fp; @@ -303,11 +303,13 @@ static int store_updated_refs(const char *url, const char *remote_name, char note[1024]; const char *what, *kind; struct ref *rm; - char *filename = git_path("FETCH_HEAD"); + char *url, *filename = git_path("FETCH_HEAD"); fp = fopen(filename, "a"); if (!fp) return error("cannot open %s: %s\n", filename, strerror(errno)); + + url = transport_anonymize_url(raw_url); for (rm = ref_map; rm; rm = rm->next) { struct ref *ref = NULL; @@ -358,12 +360,18 @@ static int store_updated_refs(const char *url, const char *remote_name, kind); note_len += sprintf(note + note_len, "'%s' of ", what); } - note_len += sprintf(note + note_len, "%.*s", url_len, url); - fprintf(fp, "%s\t%s\t%s\n", + note[note_len] = '\0'; + fprintf(fp, "%s\t%s\t%s", sha1_to_hex(commit ? commit->object.sha1 : rm->old_sha1), rm->merge ? "" : "not-for-merge", note); + for (i = 0; i < url_len; ++i) + if ('\n' == url[i]) + fputs("\\n", fp); + else + fputc(url[i], fp); + fputc('\n', fp); if (ref) rc |= update_local_ref(ref, what, note); @@ -381,6 +389,7 @@ static int store_updated_refs(const char *url, const char *remote_name, fprintf(stderr, " %s\n", note); } } + free(url); fclose(fp); if (rc & STORE_REF_ERROR_DF_CONFLICT) error("some local refs could not be updated; try running\n" @@ -630,7 +639,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) for (i = 1; i < argc; i++) strbuf_addf(&default_rla, " %s", argv[i]); - argc = parse_options(argc, argv, + argc = parse_options(argc, argv, prefix, builtin_fetch_options, builtin_fetch_usage, 0); if (argc == 0) diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index a7883690d7..fbf9582e66 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -351,7 +351,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) struct option options[] = { OPT_BOOLEAN(0, "log", &merge_summary, "populate log with the shortlog"), OPT_BOOLEAN(0, "summary", &merge_summary, "alias for --log"), - OPT_STRING('F', "file", &inpath, "file", "file to read from"), + OPT_FILENAME('F', "file", &inpath, "file to read from"), OPT_END() }; @@ -360,7 +360,8 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) int ret; git_config(fmt_merge_msg_config, NULL); - argc = parse_options(argc, argv, options, fmt_merge_msg_usage, 0); + argc = parse_options(argc, argv, prefix, options, fmt_merge_msg_usage, + 0); if (argc > 0) usage_with_options(fmt_merge_msg_usage, options); diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index d091e04af9..784733b25d 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -905,7 +905,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) OPT_END(), }; - parse_options(argc, argv, opts, for_each_ref_usage, 0); + parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0); if (maxcount < 0) { error("invalid --count argument: `%d'", maxcount); usage_with_options(for_each_ref_usage, opts); diff --git a/builtin-fsck.c b/builtin-fsck.c index 6436bc2248..7da706cac3 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -590,7 +590,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) errors_found = 0; - argc = parse_options(argc, argv, fsck_opts, fsck_usage, 0); + argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); if (write_lost_and_found) { check_full = 1; include_reflogs = 0; diff --git a/builtin-gc.c b/builtin-gc.c index fc556ed7f3..7d3e9cc7a0 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -194,7 +194,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (pack_refs < 0) pack_refs = !is_bare_repository(); - argc = parse_options(argc, argv, builtin_gc_options, builtin_gc_usage, 0); + argc = parse_options(argc, argv, prefix, builtin_gc_options, + builtin_gc_usage, 0); if (argc > 0) usage_with_options(builtin_gc_usage, builtin_gc_options); diff --git a/builtin-grep.c b/builtin-grep.c index f88a912ace..73fc922c49 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -10,6 +10,7 @@ #include "tag.h" #include "tree-walk.h" #include "builtin.h" +#include "parse-options.h" #include "grep.h" #ifndef NO_EXTERNAL_GREP @@ -20,7 +21,10 @@ #endif #endif -static int builtin_grep; +static char const * const grep_usage[] = { + "git grep [options] [-e] <pattern> [<rev>...] [[--] path...]", + NULL +}; static int grep_config(const char *var, const char *value, void *cb) { @@ -432,7 +436,8 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) } #endif -static int grep_cache(struct grep_opt *opt, const char **paths, int cached) +static int grep_cache(struct grep_opt *opt, const char **paths, int cached, + int external_grep_allowed) { int hit = 0; int nr; @@ -444,7 +449,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) * we grep through the checked-out files. It tends to * be a lot more optimized */ - if (!cached && !builtin_grep) { + if (!cached && external_grep_allowed) { hit = external_grep(opt, paths, cached); if (hit >= 0) return hit; @@ -560,25 +565,182 @@ static int grep_object(struct grep_opt *opt, const char **paths, die("unable to grep from object of type %s", typename(obj->type)); } -static const char builtin_grep_usage[] = -"git grep <option>* [-e] <pattern> <rev>* [[--] <path>...]"; +static int context_callback(const struct option *opt, const char *arg, + int unset) +{ + struct grep_opt *grep_opt = opt->value; + int value; + const char *endp; + + if (unset) { + grep_opt->pre_context = grep_opt->post_context = 0; + return 0; + } + value = strtol(arg, (char **)&endp, 10); + if (*endp) { + return error("switch `%c' expects a numerical value", + opt->short_name); + } + grep_opt->pre_context = grep_opt->post_context = value; + return 0; +} + +static int file_callback(const struct option *opt, const char *arg, int unset) +{ + struct grep_opt *grep_opt = opt->value; + FILE *patterns; + int lno = 0; + struct strbuf sb; + + patterns = fopen(arg, "r"); + if (!patterns) + die("'%s': %s", arg, strerror(errno)); + while (strbuf_getline(&sb, patterns, '\n') == 0) { + /* ignore empty line like grep does */ + if (sb.len == 0) + continue; + append_grep_pattern(grep_opt, strbuf_detach(&sb, NULL), arg, + ++lno, GREP_PATTERN); + } + fclose(patterns); + strbuf_release(&sb); + return 0; +} + +static int not_callback(const struct option *opt, const char *arg, int unset) +{ + struct grep_opt *grep_opt = opt->value; + append_grep_pattern(grep_opt, "--not", "command line", 0, GREP_NOT); + return 0; +} + +static int and_callback(const struct option *opt, const char *arg, int unset) +{ + struct grep_opt *grep_opt = opt->value; + append_grep_pattern(grep_opt, "--and", "command line", 0, GREP_AND); + return 0; +} + +static int open_callback(const struct option *opt, const char *arg, int unset) +{ + struct grep_opt *grep_opt = opt->value; + append_grep_pattern(grep_opt, "(", "command line", 0, GREP_OPEN_PAREN); + return 0; +} + +static int close_callback(const struct option *opt, const char *arg, int unset) +{ + struct grep_opt *grep_opt = opt->value; + append_grep_pattern(grep_opt, ")", "command line", 0, GREP_CLOSE_PAREN); + return 0; +} -static const char emsg_invalid_context_len[] = -"%s: invalid context length argument"; -static const char emsg_missing_context_len[] = -"missing context length argument"; -static const char emsg_missing_argument[] = -"option requires an argument -%s"; +static int pattern_callback(const struct option *opt, const char *arg, + int unset) +{ + struct grep_opt *grep_opt = opt->value; + append_grep_pattern(grep_opt, arg, "-e option", 0, GREP_PATTERN); + return 0; +} + +static int help_callback(const struct option *opt, const char *arg, int unset) +{ + return -1; +} int cmd_grep(int argc, const char **argv, const char *prefix) { int hit = 0; int cached = 0; + int external_grep_allowed = 1; int seen_dashdash = 0; struct grep_opt opt; struct object_array list = { 0, 0, NULL }; const char **paths = NULL; int i; + int dummy; + struct option options[] = { + OPT_BOOLEAN(0, "cached", &cached, + "search in index instead of in the work tree"), + OPT_GROUP(""), + OPT_BOOLEAN('v', "invert-match", &opt.invert, + "show non-matching lines"), + OPT_BIT('i', "ignore-case", &opt.regflags, + "case insensitive matching", REG_ICASE), + OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp, + "match patterns only at word boundaries"), + OPT_SET_INT('a', "text", &opt.binary, + "process binary files as text", GREP_BINARY_TEXT), + OPT_SET_INT('I', NULL, &opt.binary, + "don't match patterns in binary files", + GREP_BINARY_NOMATCH), + OPT_GROUP(""), + OPT_BIT('E', "extended-regexp", &opt.regflags, + "use extended POSIX regular expressions", REG_EXTENDED), + OPT_NEGBIT('G', "basic-regexp", &opt.regflags, + "use basic POSIX regular expressions (default)", + REG_EXTENDED), + OPT_BOOLEAN('F', "fixed-strings", &opt.fixed, + "interpret patterns as fixed strings"), + OPT_GROUP(""), + OPT_BOOLEAN('n', NULL, &opt.linenum, "show line numbers"), + OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1), + OPT_BIT('H', NULL, &opt.pathname, "show filenames", 1), + OPT_NEGBIT(0, "full-name", &opt.relative, + "show filenames relative to top directory", 1), + OPT_BOOLEAN('l', "files-with-matches", &opt.name_only, + "show only filenames instead of matching lines"), + OPT_BOOLEAN(0, "name-only", &opt.name_only, + "synonym for --files-with-matches"), + OPT_BOOLEAN('L', "files-without-match", + &opt.unmatch_name_only, + "show only the names of files without match"), + OPT_BOOLEAN('z', "null", &opt.null_following_name, + "print NUL after filenames"), + OPT_BOOLEAN('c', "count", &opt.count, + "show the number of matches instead of matching lines"), + OPT_SET_INT(0, "color", &opt.color, "highlight matches", 1), + OPT_GROUP(""), + OPT_CALLBACK('C', NULL, &opt, "n", + "show <n> context lines before and after matches", + context_callback), + OPT_INTEGER('B', NULL, &opt.pre_context, + "show <n> context lines before matches"), + OPT_INTEGER('A', NULL, &opt.post_context, + "show <n> context lines after matches"), + OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM", + context_callback), + OPT_GROUP(""), + OPT_CALLBACK('f', NULL, &opt, "file", + "read patterns from file", file_callback), + { OPTION_CALLBACK, 'e', NULL, &opt, "pattern", + "match <pattern>", PARSE_OPT_NONEG, pattern_callback }, + { OPTION_CALLBACK, 0, "and", &opt, NULL, + "combine patterns specified with -e", + PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback }, + OPT_BOOLEAN(0, "or", &dummy, ""), + { OPTION_CALLBACK, 0, "not", &opt, NULL, "", + PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback }, + { OPTION_CALLBACK, '(', NULL, &opt, NULL, "", + PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, + open_callback }, + { OPTION_CALLBACK, ')', NULL, &opt, NULL, "", + PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, + close_callback }, + OPT_BOOLEAN(0, "all-match", &opt.all_match, + "show only matches from files that match all patterns"), + OPT_GROUP(""), +#if NO_EXTERNAL_GREP + OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed, + "allow calling of grep(1) (ignored by this build)"), +#else + OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed, + "allow calling of grep(1) (default)"), +#endif + { OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage", + PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback }, + OPT_END() + }; memset(&opt, 0, sizeof(opt)); opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0; @@ -603,227 +765,21 @@ int cmd_grep(int argc, const char **argv, const char *prefix) * unrecognized non option is the beginning of the refs list * that continues up to the -- (if exists), and then paths. */ - - while (1 < argc) { - const char *arg = argv[1]; - argc--; argv++; - if (!strcmp("--cached", arg)) { - cached = 1; - continue; - } - if (!strcmp("--no-ext-grep", arg)) { - builtin_grep = 1; - continue; - } - if (!strcmp("-a", arg) || - !strcmp("--text", arg)) { - opt.binary = GREP_BINARY_TEXT; - continue; - } - if (!strcmp("-i", arg) || - !strcmp("--ignore-case", arg)) { - opt.regflags |= REG_ICASE; - continue; - } - if (!strcmp("-I", arg)) { - opt.binary = GREP_BINARY_NOMATCH; - continue; - } - if (!strcmp("-v", arg) || - !strcmp("--invert-match", arg)) { - opt.invert = 1; - continue; - } - if (!strcmp("-E", arg) || - !strcmp("--extended-regexp", arg)) { - opt.regflags |= REG_EXTENDED; - continue; - } - if (!strcmp("-F", arg) || - !strcmp("--fixed-strings", arg)) { - opt.fixed = 1; - continue; - } - if (!strcmp("-G", arg) || - !strcmp("--basic-regexp", arg)) { - opt.regflags &= ~REG_EXTENDED; - continue; - } - if (!strcmp("-n", arg)) { - opt.linenum = 1; - continue; - } - if (!strcmp("-h", arg)) { - opt.pathname = 0; - continue; - } - if (!strcmp("-H", arg)) { - opt.pathname = 1; - continue; - } - if (!strcmp("-l", arg) || - !strcmp("--name-only", arg) || - !strcmp("--files-with-matches", arg)) { - opt.name_only = 1; - continue; - } - if (!strcmp("-L", arg) || - !strcmp("--files-without-match", arg)) { - opt.unmatch_name_only = 1; - continue; - } - if (!strcmp("-z", arg) || - !strcmp("--null", arg)) { - opt.null_following_name = 1; - continue; - } - if (!strcmp("-c", arg) || - !strcmp("--count", arg)) { - opt.count = 1; - continue; - } - if (!strcmp("-w", arg) || - !strcmp("--word-regexp", arg)) { - opt.word_regexp = 1; - continue; - } - if (!prefixcmp(arg, "-A") || - !prefixcmp(arg, "-B") || - !prefixcmp(arg, "-C") || - (arg[0] == '-' && '1' <= arg[1] && arg[1] <= '9')) { - unsigned num; - const char *scan; - switch (arg[1]) { - case 'A': case 'B': case 'C': - if (!arg[2]) { - if (argc <= 1) - die(emsg_missing_context_len); - scan = *++argv; - argc--; - } - else - scan = arg + 2; - break; - default: - scan = arg + 1; - break; - } - if (strtoul_ui(scan, 10, &num)) - die(emsg_invalid_context_len, scan); - switch (arg[1]) { - case 'A': - opt.post_context = num; - break; - default: - case 'C': - opt.post_context = num; - case 'B': - opt.pre_context = num; - break; - } - continue; - } - if (!strcmp("-f", arg)) { - FILE *patterns; - int lno = 0; - char buf[1024]; - if (argc <= 1) - die(emsg_missing_argument, arg); - patterns = fopen(argv[1], "r"); - if (!patterns) - die("'%s': %s", argv[1], strerror(errno)); - while (fgets(buf, sizeof(buf), patterns)) { - int len = strlen(buf); - if (len && buf[len-1] == '\n') - buf[len-1] = 0; - /* ignore empty line like grep does */ - if (!buf[0]) - continue; - append_grep_pattern(&opt, xstrdup(buf), - argv[1], ++lno, - GREP_PATTERN); - } - fclose(patterns); - argv++; - argc--; - continue; - } - if (!strcmp("--not", arg)) { - append_grep_pattern(&opt, arg, "command line", 0, - GREP_NOT); - continue; - } - if (!strcmp("--and", arg)) { - append_grep_pattern(&opt, arg, "command line", 0, - GREP_AND); - continue; - } - if (!strcmp("--or", arg)) - continue; /* no-op */ - if (!strcmp("(", arg)) { - append_grep_pattern(&opt, arg, "command line", 0, - GREP_OPEN_PAREN); - continue; - } - if (!strcmp(")", arg)) { - append_grep_pattern(&opt, arg, "command line", 0, - GREP_CLOSE_PAREN); - continue; - } - if (!strcmp("--all-match", arg)) { - opt.all_match = 1; - continue; - } - if (!strcmp("-e", arg)) { - if (1 < argc) { - append_grep_pattern(&opt, argv[1], - "-e option", 0, - GREP_PATTERN); - argv++; - argc--; - continue; - } - die(emsg_missing_argument, arg); - } - if (!strcmp("--full-name", arg)) { - opt.relative = 0; - continue; - } - if (!strcmp("--color", arg)) { - opt.color = 1; - continue; - } - if (!strcmp("--no-color", arg)) { - opt.color = 0; - continue; - } - if (!strcmp("--", arg)) { - /* later processing wants to have this at argv[1] */ - argv--; - argc++; - break; - } - if (*arg == '-') - usage(builtin_grep_usage); - - /* First unrecognized non-option token */ - if (!opt.pattern_list) { - append_grep_pattern(&opt, arg, "command line", 0, - GREP_PATTERN); - break; - } - else { - /* We are looking at the first path or rev; - * it is found at argv[1] after leaving the - * loop. - */ - argc++; argv--; - break; - } + argc = parse_options(argc, argv, prefix, options, grep_usage, + PARSE_OPT_KEEP_DASHDASH | + PARSE_OPT_STOP_AT_NON_OPTION | + PARSE_OPT_NO_INTERNAL_HELP); + + /* First unrecognized non-option token */ + if (argc > 0 && !opt.pattern_list) { + append_grep_pattern(&opt, argv[0], "command line", 0, + GREP_PATTERN); + argv++; + argc--; } if (opt.color && !opt.color_external) - builtin_grep = 1; + external_grep_allowed = 0; if (!opt.pattern_list) die("no pattern given."); if ((opt.regflags != REG_NEWLINE) && opt.fixed) @@ -831,7 +787,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) compile_grep_patterns(&opt); /* Check revs and then paths */ - for (i = 1; i < argc; i++) { + for (i = 0; i < argc; i++) { const char *arg = argv[i]; unsigned char sha1[20]; /* Is it a rev? */ @@ -874,7 +830,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) if (!list.nr) { if (!cached) setup_work_tree(); - return !grep_cache(&opt, paths, cached); + return !grep_cache(&opt, paths, cached, external_grep_allowed); } if (cached) diff --git a/builtin-help.c b/builtin-help.c index 67dda3e6e6..6e53b23833 100644 --- a/builtin-help.c +++ b/builtin-help.c @@ -80,10 +80,9 @@ static int check_emacsclient_version(void) ec_process.argv = argv_ec; ec_process.err = -1; ec_process.stdout_to_stderr = 1; - if (start_command(&ec_process)) { - fprintf(stderr, "Failed to start emacsclient.\n"); - return -1; - } + if (start_command(&ec_process)) + return error("Failed to start emacsclient."); + strbuf_read(&buffer, ec_process.err, 20); close(ec_process.err); @@ -94,20 +93,17 @@ static int check_emacsclient_version(void) finish_command(&ec_process); if (prefixcmp(buffer.buf, "emacsclient")) { - fprintf(stderr, "Failed to parse emacsclient version.\n"); strbuf_release(&buffer); - return -1; + return error("Failed to parse emacsclient version."); } strbuf_remove(&buffer, 0, strlen("emacsclient")); version = atoi(buffer.buf); if (version < 22) { - fprintf(stderr, - "emacsclient version '%d' too old (< 22).\n", - version); strbuf_release(&buffer); - return -1; + return error("emacsclient version '%d' too old (< 22).", + version); } strbuf_release(&buffer); @@ -423,7 +419,7 @@ int cmd_help(int argc, const char **argv, const char *prefix) setup_git_directory_gently(&nongit); git_config(git_help_config, NULL); - argc = parse_options(argc, argv, builtin_help_options, + argc = parse_options(argc, argv, prefix, builtin_help_options, builtin_help_usage, 0); if (show_all) { diff --git a/builtin-log.c b/builtin-log.c index f10cfebdbb..0d34050556 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -18,6 +18,7 @@ #include "shortlog.h" #include "remote.h" #include "string-list.h" +#include "parse-options.h" /* Set a default date-time format for git log ("log.date" config variable) */ static const char *default_date_mode = NULL; @@ -619,7 +620,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, struct shortlog log; struct strbuf sb = STRBUF_INIT; int i; - const char *encoding = "utf-8"; + const char *encoding = "UTF-8"; struct diff_options opts; int need_8bit_cte = 0; struct commit *commit = NULL; @@ -740,27 +741,179 @@ static const char *set_outdir(const char *prefix, const char *output_directory) output_directory)); } +static const char * const builtin_format_patch_usage[] = { + "git format-patch [options] [<since> | <revision range>]", + NULL +}; + +static int keep_subject = 0; + +static int keep_callback(const struct option *opt, const char *arg, int unset) +{ + ((struct rev_info *)opt->value)->total = -1; + keep_subject = 1; + return 0; +} + +static int subject_prefix = 0; + +static int subject_prefix_callback(const struct option *opt, const char *arg, + int unset) +{ + subject_prefix = 1; + ((struct rev_info *)opt->value)->subject_prefix = arg; + return 0; +} + +static int numbered_cmdline_opt = 0; + +static int numbered_callback(const struct option *opt, const char *arg, + int unset) +{ + *(int *)opt->value = numbered_cmdline_opt = unset ? 0 : 1; + if (unset) + auto_number = 0; + return 0; +} + +static int no_numbered_callback(const struct option *opt, const char *arg, + int unset) +{ + return numbered_callback(opt, arg, 1); +} + +static int output_directory_callback(const struct option *opt, const char *arg, + int unset) +{ + const char **dir = (const char **)opt->value; + if (*dir) + die("Two output directories?"); + *dir = arg; + return 0; +} + +static int thread_callback(const struct option *opt, const char *arg, int unset) +{ + int *thread = (int *)opt->value; + if (unset) + *thread = 0; + else if (!arg || !strcmp(arg, "shallow")) + *thread = THREAD_SHALLOW; + else if (!strcmp(arg, "deep")) + *thread = THREAD_DEEP; + else + return 1; + return 0; +} + +static int attach_callback(const struct option *opt, const char *arg, int unset) +{ + struct rev_info *rev = (struct rev_info *)opt->value; + if (unset) + rev->mime_boundary = NULL; + else if (arg) + rev->mime_boundary = arg; + else + rev->mime_boundary = git_version_string; + rev->no_inline = unset ? 0 : 1; + return 0; +} + +static int inline_callback(const struct option *opt, const char *arg, int unset) +{ + struct rev_info *rev = (struct rev_info *)opt->value; + if (unset) + rev->mime_boundary = NULL; + else if (arg) + rev->mime_boundary = arg; + else + rev->mime_boundary = git_version_string; + rev->no_inline = 0; + return 0; +} + +static int header_callback(const struct option *opt, const char *arg, int unset) +{ + add_header(arg); + return 0; +} + +static int cc_callback(const struct option *opt, const char *arg, int unset) +{ + ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc); + extra_cc[extra_cc_nr++] = xstrdup(arg); + return 0; +} + int cmd_format_patch(int argc, const char **argv, const char *prefix) { struct commit *commit; struct commit **list = NULL; struct rev_info rev; - int nr = 0, total, i, j; + int nr = 0, total, i; int use_stdout = 0; int start_number = -1; - int keep_subject = 0; int numbered_files = 0; /* _just_ numbers */ - int subject_prefix = 0; int ignore_if_in_upstream = 0; int cover_letter = 0; int boundary_count = 0; int no_binary_diff = 0; - int numbered_cmdline_opt = 0; struct commit *origin = NULL, *head = NULL; const char *in_reply_to = NULL; struct patch_ids ids; char *add_signoff = NULL; struct strbuf buf = STRBUF_INIT; + const struct option builtin_format_patch_options[] = { + { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL, + "use [PATCH n/m] even with a single patch", + PARSE_OPT_NOARG, numbered_callback }, + { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL, + "use [PATCH] even with multiple patches", + PARSE_OPT_NOARG, no_numbered_callback }, + OPT_BOOLEAN('s', "signoff", &do_signoff, "add Signed-off-by:"), + OPT_BOOLEAN(0, "stdout", &use_stdout, + "print patches to standard out"), + OPT_BOOLEAN(0, "cover-letter", &cover_letter, + "generate a cover letter"), + OPT_BOOLEAN(0, "numbered-files", &numbered_files, + "use simple number sequence for output file names"), + OPT_STRING(0, "suffix", &fmt_patch_suffix, "sfx", + "use <sfx> instead of '.patch'"), + OPT_INTEGER(0, "start-number", &start_number, + "start numbering patches at <n> instead of 1"), + { OPTION_CALLBACK, 0, "subject-prefix", &rev, "prefix", + "Use [<prefix>] instead of [PATCH]", + PARSE_OPT_NONEG, subject_prefix_callback }, + { OPTION_CALLBACK, 'o', "output-directory", &output_directory, + "dir", "store resulting files in <dir>", + PARSE_OPT_NONEG, output_directory_callback }, + { OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL, + "don't strip/add [PATCH]", + PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback }, + OPT_BOOLEAN(0, "no-binary", &no_binary_diff, + "don't output binary diffs"), + OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream, + "don't include a patch matching a commit upstream"), + OPT_GROUP("Messaging"), + { OPTION_CALLBACK, 0, "add-header", NULL, "header", + "add email header", PARSE_OPT_NONEG, + header_callback }, + { OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header", + PARSE_OPT_NONEG, cc_callback }, + OPT_STRING(0, "in-reply-to", &in_reply_to, "message-id", + "make first mail a reply to <message-id>"), + { OPTION_CALLBACK, 0, "attach", &rev, "boundary", + "attach the patch", PARSE_OPT_OPTARG, + attach_callback }, + { OPTION_CALLBACK, 0, "inline", &rev, "boundary", + "inline the patch", + PARSE_OPT_OPTARG | PARSE_OPT_NONEG, + inline_callback }, + { OPTION_CALLBACK, 0, "thread", &thread, "style", + "enable message threading, styles: shallow, deep", + PARSE_OPT_OPTARG, thread_callback }, + OPT_END() + }; git_config(git_format_config, NULL); init_revisions(&rev, prefix); @@ -783,102 +936,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) * like "git format-patch -o a123 HEAD^.." may fail; a123 is * possibly a valid SHA1. */ - for (i = 1, j = 1; i < argc; i++) { - if (!strcmp(argv[i], "--stdout")) - use_stdout = 1; - else if (!strcmp(argv[i], "-n") || - !strcmp(argv[i], "--numbered")) { - numbered = 1; - numbered_cmdline_opt = 1; - } - else if (!strcmp(argv[i], "-N") || - !strcmp(argv[i], "--no-numbered")) { - numbered = 0; - auto_number = 0; - } - else if (!prefixcmp(argv[i], "--start-number=")) - start_number = strtol(argv[i] + 15, NULL, 10); - else if (!strcmp(argv[i], "--numbered-files")) - numbered_files = 1; - else if (!strcmp(argv[i], "--start-number")) { - i++; - if (i == argc) - die("Need a number for --start-number"); - start_number = strtol(argv[i], NULL, 10); - } - else if (!prefixcmp(argv[i], "--cc=")) { - ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc); - extra_cc[extra_cc_nr++] = xstrdup(argv[i] + 5); - } - else if (!strcmp(argv[i], "-k") || - !strcmp(argv[i], "--keep-subject")) { - keep_subject = 1; - rev.total = -1; - } - else if (!strcmp(argv[i], "--output-directory") || - !strcmp(argv[i], "-o")) { - i++; - if (argc <= i) - die("Which directory?"); - if (output_directory) - die("Two output directories?"); - output_directory = argv[i]; - } - else if (!strcmp(argv[i], "--signoff") || - !strcmp(argv[i], "-s")) { - do_signoff = 1; - } - else if (!strcmp(argv[i], "--attach")) { - rev.mime_boundary = git_version_string; - rev.no_inline = 1; - } - else if (!prefixcmp(argv[i], "--attach=")) { - rev.mime_boundary = argv[i] + 9; - rev.no_inline = 1; - } - else if (!strcmp(argv[i], "--no-attach")) { - rev.mime_boundary = NULL; - rev.no_inline = 0; - } - else if (!strcmp(argv[i], "--inline")) { - rev.mime_boundary = git_version_string; - rev.no_inline = 0; - } - else if (!prefixcmp(argv[i], "--inline=")) { - rev.mime_boundary = argv[i] + 9; - rev.no_inline = 0; - } - else if (!strcmp(argv[i], "--ignore-if-in-upstream")) - ignore_if_in_upstream = 1; - else if (!strcmp(argv[i], "--thread") - || !strcmp(argv[i], "--thread=shallow")) - thread = THREAD_SHALLOW; - else if (!strcmp(argv[i], "--thread=deep")) - thread = THREAD_DEEP; - else if (!strcmp(argv[i], "--no-thread")) - thread = 0; - else if (!prefixcmp(argv[i], "--in-reply-to=")) - in_reply_to = argv[i] + 14; - else if (!strcmp(argv[i], "--in-reply-to")) { - i++; - if (i == argc) - die("Need a Message-Id for --in-reply-to"); - in_reply_to = argv[i]; - } else if (!prefixcmp(argv[i], "--subject-prefix=")) { - subject_prefix = 1; - rev.subject_prefix = argv[i] + 17; - } else if (!prefixcmp(argv[i], "--suffix=")) - fmt_patch_suffix = argv[i] + 9; - else if (!strcmp(argv[i], "--cover-letter")) - cover_letter = 1; - else if (!strcmp(argv[i], "--no-binary")) - no_binary_diff = 1; - else if (!prefixcmp(argv[i], "--add-header=")) - add_header(argv[i] + 13); - else - argv[j++] = argv[i]; - } - argc = j; + argc = parse_options(argc, argv, prefix, builtin_format_patch_options, + builtin_format_patch_usage, + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN); if (do_signoff) { const char *committer; diff --git a/builtin-ls-files.c b/builtin-ls-files.c index da2daf45ac..2312866605 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -454,7 +454,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) OPT_BIT(0, "directory", &dir.flags, "show 'other' directories' name only", DIR_SHOW_OTHER_DIRECTORIES), - OPT_BIT(0, "no-empty-directory", &dir.flags, + OPT_NEGBIT(0, "empty-directory", &dir.flags, "don't show empty directories", DIR_HIDE_EMPTY_DIRECTORIES), OPT_BOOLEAN('u', "unmerged", &show_unmerged, @@ -486,7 +486,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) prefix_offset = strlen(prefix); git_config(git_default_config, NULL); - argc = parse_options(argc, argv, builtin_ls_files_options, + argc = parse_options(argc, argv, prefix, builtin_ls_files_options, ls_files_usage, 0); if (show_tag || show_valid_bit) { tag_cached = "H "; diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index 1eeeb4de6d..92637ac0ba 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -193,8 +193,7 @@ static void handle_content_type(struct strbuf *line) *content_top = boundary; boundary = NULL; } - if (slurp_attr(line->buf, "charset=", &charset)) - strbuf_tolower(&charset); + slurp_attr(line->buf, "charset=", &charset); if (boundary) { strbuf_release(boundary); @@ -481,7 +480,7 @@ static const char *guess_charset(const struct strbuf *line, const char *target_c if (is_utf8(line->buf)) return NULL; } - return "latin1"; + return "ISO8859-1"; } static void convert_to_utf8(struct strbuf *line, const char *charset) @@ -494,7 +493,7 @@ static void convert_to_utf8(struct strbuf *line, const char *charset) return; } - if (!strcmp(metainfo_charset, charset)) + if (!strcasecmp(metainfo_charset, charset)) return; out = reencode_string(line->buf, metainfo_charset, charset); if (!out) @@ -550,7 +549,6 @@ static int decode_header_bq(struct strbuf *it) if (cp + 3 - it->buf > it->len) goto decode_header_bq_out; strbuf_add(&charset_q, ep, cp - ep); - strbuf_tolower(&charset_q); encoding = cp[1]; if (!encoding || cp[2] != '?') @@ -944,7 +942,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix) */ git_config(git_default_config, NULL); - def_charset = (git_commit_encoding ? git_commit_encoding : "utf-8"); + def_charset = (git_commit_encoding ? git_commit_encoding : "UTF-8"); metainfo_charset = def_charset; while (1 < argc && argv[1][0] == '-') { diff --git a/builtin-merge-base.c b/builtin-merge-base.c index 03fc1c2114..a6ec2f7ab7 100644 --- a/builtin-merge-base.c +++ b/builtin-merge-base.c @@ -53,7 +53,7 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix) }; git_config(git_default_config, NULL); - argc = parse_options(argc, argv, options, merge_base_usage, 0); + argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0); if (argc < 2) usage_with_options(merge_base_usage, options); rev = xmalloc(argc * sizeof(*rev)); diff --git a/builtin-merge-file.c b/builtin-merge-file.c index 96edb97a83..afd2ea7a73 100644 --- a/builtin-merge-file.c +++ b/builtin-merge-file.c @@ -48,7 +48,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix) merge_style = git_xmerge_style; } - argc = parse_options(argc, argv, options, merge_file_usage, 0); + argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0); if (argc != 3) usage_with_options(merge_file_usage, options); if (quiet) { diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c index 703045bfc8..d26a96e486 100644 --- a/builtin-merge-recursive.c +++ b/builtin-merge-recursive.c @@ -45,8 +45,9 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix) bases[bases_count++] = sha; } else - warning("Cannot handle more than %zu bases. " - "Ignoring %s.", ARRAY_SIZE(bases)-1, argv[i]); + warning("Cannot handle more than %d bases. " + "Ignoring %s.", + (int)ARRAY_SIZE(bases)-1, argv[i]); } if (argc - i != 3) /* "--" "<head>" "<remote>" */ die("Not handling anything other than two heads merge."); diff --git a/builtin-merge.c b/builtin-merge.c index 9e9bd526c3..793f2f4a18 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -462,7 +462,7 @@ static int git_merge_config(const char *k, const char *v, void *cb) argv = xrealloc(argv, sizeof(*argv) * (argc + 2)); memmove(argv + 1, argv, sizeof(*argv) * (argc + 1)); argc++; - parse_options(argc, argv, builtin_merge_options, + parse_options(argc, argv, NULL, builtin_merge_options, builtin_merge_usage, 0); free(buf); } @@ -858,7 +858,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; - argc = parse_options(argc, argv, builtin_merge_options, + argc = parse_options(argc, argv, prefix, builtin_merge_options, builtin_merge_usage, 0); if (verbosity < 0) show_diffstat = 0; diff --git a/builtin-mktree.c b/builtin-mktree.c new file mode 100644 index 0000000000..098395fda1 --- /dev/null +++ b/builtin-mktree.c @@ -0,0 +1,190 @@ +/* + * GIT - the stupid content tracker + * + * Copyright (c) Junio C Hamano, 2006, 2009 + */ +#include "builtin.h" +#include "quote.h" +#include "tree.h" +#include "parse-options.h" + +static struct treeent { + unsigned mode; + unsigned char sha1[20]; + int len; + char name[FLEX_ARRAY]; +} **entries; +static int alloc, used; + +static void append_to_tree(unsigned mode, unsigned char *sha1, char *path) +{ + struct treeent *ent; + int len = strlen(path); + if (strchr(path, '/')) + die("path %s contains slash", path); + + if (alloc <= used) { + alloc = alloc_nr(used); + entries = xrealloc(entries, sizeof(*entries) * alloc); + } + ent = entries[used++] = xmalloc(sizeof(**entries) + len + 1); + ent->mode = mode; + ent->len = len; + hashcpy(ent->sha1, sha1); + memcpy(ent->name, path, len+1); +} + +static int ent_compare(const void *a_, const void *b_) +{ + struct treeent *a = *(struct treeent **)a_; + struct treeent *b = *(struct treeent **)b_; + return base_name_compare(a->name, a->len, a->mode, + b->name, b->len, b->mode); +} + +static void write_tree(unsigned char *sha1) +{ + struct strbuf buf; + size_t size; + int i; + + qsort(entries, used, sizeof(*entries), ent_compare); + for (size = i = 0; i < used; i++) + size += 32 + entries[i]->len; + + strbuf_init(&buf, size); + for (i = 0; i < used; i++) { + struct treeent *ent = entries[i]; + strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0'); + strbuf_add(&buf, ent->sha1, 20); + } + + write_sha1_file(buf.buf, buf.len, tree_type, sha1); +} + +static const char *mktree_usage[] = { + "git mktree [-z] [--missing] [--batch]", + NULL +}; + +static void mktree_line(char *buf, size_t len, int line_termination, int allow_missing) +{ + char *ptr, *ntr; + unsigned mode; + enum object_type mode_type; /* object type derived from mode */ + enum object_type obj_type; /* object type derived from sha */ + char *path; + unsigned char sha1[20]; + + ptr = buf; + /* + * Read non-recursive ls-tree output format: + * mode SP type SP sha1 TAB name + */ + mode = strtoul(ptr, &ntr, 8); + if (ptr == ntr || !ntr || *ntr != ' ') + die("input format error: %s", buf); + ptr = ntr + 1; /* type */ + ntr = strchr(ptr, ' '); + if (!ntr || buf + len <= ntr + 40 || + ntr[41] != '\t' || + get_sha1_hex(ntr + 1, sha1)) + die("input format error: %s", buf); + + /* It is perfectly normal if we do not have a commit from a submodule */ + if (S_ISGITLINK(mode)) + allow_missing = 1; + + + *ntr++ = 0; /* now at the beginning of SHA1 */ + + path = ntr + 41; /* at the beginning of name */ + if (line_termination && path[0] == '"') { + struct strbuf p_uq = STRBUF_INIT; + if (unquote_c_style(&p_uq, path, NULL)) + die("invalid quoting"); + path = strbuf_detach(&p_uq, NULL); + } + + /* + * Object type is redundantly derivable three ways. + * These should all agree. + */ + mode_type = object_type(mode); + if (mode_type != type_from_string(ptr)) { + die("entry '%s' object type (%s) doesn't match mode type (%s)", + path, ptr, typename(mode_type)); + } + + /* Check the type of object identified by sha1 */ + obj_type = sha1_object_info(sha1, NULL); + if (obj_type < 0) { + if (allow_missing) { + ; /* no problem - missing objects are presumed to be of the right type */ + } else { + die("entry '%s' object %s is unavailable", path, sha1_to_hex(sha1)); + } + } else { + if (obj_type != mode_type) { + /* + * The object exists but is of the wrong type. + * This is a problem regardless of allow_missing + * because the new tree entry will never be correct. + */ + die("entry '%s' object %s is a %s but specified type was (%s)", + path, sha1_to_hex(sha1), typename(obj_type), typename(mode_type)); + } + } + + append_to_tree(mode, sha1, path); +} + +int cmd_mktree(int ac, const char **av, const char *prefix) +{ + struct strbuf sb = STRBUF_INIT; + unsigned char sha1[20]; + int line_termination = '\n'; + int allow_missing = 0; + int is_batch_mode = 0; + int got_eof = 0; + + const struct option option[] = { + OPT_SET_INT('z', NULL, &line_termination, "input is NUL terminated", '\0'), + OPT_SET_INT( 0 , "missing", &allow_missing, "allow missing objects", 1), + OPT_SET_INT( 0 , "batch", &is_batch_mode, "allow creation of more than one tree", 1), + OPT_END() + }; + + ac = parse_options(ac, av, prefix, option, mktree_usage, 0); + + while (!got_eof) { + while (1) { + if (strbuf_getline(&sb, stdin, line_termination) == EOF) { + got_eof = 1; + break; + } + if (sb.buf[0] == '\0') { + /* empty lines denote tree boundaries in batch mode */ + if (is_batch_mode) + break; + die("input format error: (blank line only valid in batch mode)"); + } + mktree_line(sb.buf, sb.len, line_termination, allow_missing); + } + if (is_batch_mode && got_eof && used < 1) { + /* + * Execution gets here if the last tree entry is terminated with a + * new-line. The final new-line has been made optional to be + * consistent with the original non-batch behaviour of mktree. + */ + ; /* skip creating an empty tree */ + } else { + write_tree(sha1); + puts(sha1_to_hex(sha1)); + fflush(stdout); + } + used=0; /* reset tree entry buffer for re-use in batch mode */ + } + strbuf_release(&sb); + exit(0); +} diff --git a/builtin-mv.c b/builtin-mv.c index 01270fefdf..8b81d4b51d 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -72,7 +72,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die("index file corrupt"); - argc = parse_options(argc, argv, builtin_mv_options, builtin_mv_usage, 0); + argc = parse_options(argc, argv, prefix, builtin_mv_options, + builtin_mv_usage, 0); if (--argc < 1) usage_with_options(builtin_mv_usage, builtin_mv_options); diff --git a/builtin-name-rev.c b/builtin-name-rev.c index 08c8aabf94..06a38ac8c1 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -238,7 +238,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) }; git_config(git_default_config, NULL); - argc = parse_options(argc, argv, opts, name_rev_usage, 0); + argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0); if (!!all + !!transform_stdin + !!argc > 1) { error("Specify either a list, or --all, not both!"); usage_with_options(name_rev_usage, opts); diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 34246df4ec..091860b2e3 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -15,7 +15,7 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) OPT_BIT(0, "prune", &flags, "prune loose refs (default)", PACK_REFS_PRUNE), OPT_END(), }; - if (parse_options(argc, argv, opts, pack_refs_usage, 0)) + if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0)) usage_with_options(pack_refs_usage, opts); return pack_refs(flags); } diff --git a/builtin-prune.c b/builtin-prune.c index 145ba83651..0ed9cce4a2 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -142,7 +142,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) save_commit_buffer = 0; init_revisions(&revs, prefix); - argc = parse_options(argc, argv, options, prune_usage, 0); + argc = parse_options(argc, argv, prefix, options, prune_usage, 0); while (argc--) { unsigned char sha1[20]; const char *name = *argv++; diff --git a/builtin-push.c b/builtin-push.c index 2eabcd3bdf..c869974013 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -198,7 +198,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) OPT_END() }; - argc = parse_options(argc, argv, options, push_usage, 0); + argc = parse_options(argc, argv, prefix, options, push_usage, 0); if (tags) add_refspec("refs/tags/*"); diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c index 0b08da9b59..33d345dc39 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -192,7 +192,6 @@ static int run_receive_hook(const char *hook_name) static int run_update_hook(struct command *cmd) { static const char update_hook[] = "hooks/update"; - struct child_process proc; const char *argv[5]; if (access(update_hook, X_OK) < 0) @@ -204,12 +203,9 @@ static int run_update_hook(struct command *cmd) argv[3] = sha1_to_hex(cmd->new_sha1); argv[4] = NULL; - memset(&proc, 0, sizeof(proc)); - proc.argv = argv; - proc.no_stdin = 1; - proc.stdout_to_stderr = 1; - - return hook_status(run_command(&proc), update_hook); + return hook_status(run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | + RUN_COMMAND_STDOUT_TO_STDERR), + update_hook); } static int is_ref_checked_out(const char *ref) diff --git a/builtin-remote.c b/builtin-remote.c index fda9a54a0c..dfc0b9e706 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -79,7 +79,8 @@ static int add(int argc, const char **argv) OPT_END() }; - argc = parse_options(argc, argv, options, builtin_remote_usage, 0); + argc = parse_options(argc, argv, NULL, options, builtin_remote_usage, + 0); if (argc < 2) usage_with_options(builtin_remote_usage, options); @@ -294,17 +295,14 @@ static int get_push_ref_states(const struct ref *remote_refs, struct ref_states *states) { struct remote *remote = states->remote; - struct ref *ref, *local_refs, *push_map, **push_tail; + struct ref *ref, *local_refs, *push_map; if (remote->mirror) return 0; local_refs = get_local_heads(); push_map = copy_ref_list(remote_refs); - push_tail = &push_map; - while (*push_tail) - push_tail = &((*push_tail)->next); - match_refs(local_refs, push_map, &push_tail, remote->push_refspec_nr, + match_refs(local_refs, &push_map, remote->push_refspec_nr, remote->push_refspec, MATCH_REFS_NONE); states->push.strdup_strings = 1; @@ -986,7 +984,8 @@ static int show(int argc, const char **argv) struct string_list info_list = { NULL, 0, 0, 0 }; struct show_info info; - argc = parse_options(argc, argv, options, builtin_remote_usage, 0); + argc = parse_options(argc, argv, NULL, options, builtin_remote_usage, + 0); if (argc < 1) return show_all(); @@ -1003,9 +1002,12 @@ static int show(int argc, const char **argv) get_remote_ref_states(*argv, &states, query_flag); - printf("* remote %s\n URL: %s\n", *argv, - states.remote->url_nr > 0 ? - states.remote->url[0] : "(no URL)"); + printf("* remote %s\n", *argv); + if (states.remote->url_nr) { + for (i=0; i < states.remote->url_nr; i++) + printf(" URL: %s\n", states.remote->url[i]); + } else + printf(" URL: %s\n", "(no URL)"); if (no_query) printf(" HEAD branch: (not queried)\n"); else if (!states.heads.nr) @@ -1076,7 +1078,8 @@ static int set_head(int argc, const char **argv) "delete refs/remotes/<name>/HEAD"), OPT_END() }; - argc = parse_options(argc, argv, options, builtin_remote_usage, 0); + argc = parse_options(argc, argv, NULL, options, builtin_remote_usage, + 0); if (argc) strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]); @@ -1130,7 +1133,8 @@ static int prune(int argc, const char **argv) OPT_END() }; - argc = parse_options(argc, argv, options, builtin_remote_usage, 0); + argc = parse_options(argc, argv, NULL, options, builtin_remote_usage, + 0); if (argc < 1) usage_with_options(builtin_remote_usage, options); @@ -1220,7 +1224,7 @@ static int update(int argc, const char **argv) OPT_END() }; - argc = parse_options(argc, argv, options, builtin_remote_usage, + argc = parse_options(argc, argv, NULL, options, builtin_remote_usage, PARSE_OPT_KEEP_ARGV0); if (argc < 2) { argc = 2; @@ -1306,7 +1310,7 @@ int cmd_remote(int argc, const char **argv, const char *prefix) }; int result; - argc = parse_options(argc, argv, options, builtin_remote_usage, + argc = parse_options(argc, argv, prefix, options, builtin_remote_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (argc < 1) diff --git a/builtin-reset.c b/builtin-reset.c index 7e7ebabaa8..5fa1789d0c 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -203,7 +203,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); - argc = parse_options(argc, argv, options, git_reset_usage, + argc = parse_options(argc, argv, prefix, options, git_reset_usage, PARSE_OPT_KEEP_DASHDASH); reflog_action = args_to_str(argv); setenv("GIT_REFLOG_ACTION", reflog_action, 0); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 38a8f234de..31ea5f4aac 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -211,7 +211,7 @@ static inline int exp2i(int n) * * and P(2^n + x) < 0.5 means 2^n < 3x */ -static int estimate_bisect_steps(int all) +int estimate_bisect_steps(int all) { int n, x, e; @@ -225,20 +225,37 @@ static int estimate_bisect_steps(int all) return (e < 3 * x) ? n : n - 1; } -static void show_tried_revs(struct commit_list *tried, int stringed) +void print_commit_list(struct commit_list *list, + const char *format_cur, + const char *format_last) { - printf("bisect_tried='"); - for (;tried; tried = tried->next) { - char *format = tried->next ? "%s|" : "%s"; - printf(format, sha1_to_hex(tried->item->object.sha1)); + for ( ; list; list = list->next) { + const char *format = list->next ? format_cur : format_last; + printf(format, sha1_to_hex(list->item->object.sha1)); } - printf(stringed ? "' &&\n" : "'\n"); +} + +static void show_tried_revs(struct commit_list *tried) +{ + printf("bisect_tried='"); + print_commit_list(tried, "%s|", "%s"); + printf("'\n"); +} + +static void print_var_str(const char *var, const char *val) +{ + printf("%s='%s'\n", var, val); +} + +static void print_var_int(const char *var, int val) +{ + printf("%s=%d\n", var, val); } int show_bisect_vars(struct rev_list_info *info, int reaches, int all) { int cnt, flags = info->bisect_show_flags; - char hex[41] = "", *format; + char hex[41] = ""; struct commit_list *tried; struct rev_info *revs = info->revs; @@ -269,28 +286,14 @@ int show_bisect_vars(struct rev_list_info *info, int reaches, int all) } if (flags & BISECT_SHOW_TRIED) - show_tried_revs(tried, flags & BISECT_SHOW_STRINGED); - format = (flags & BISECT_SHOW_STRINGED) ? - "bisect_rev=%s &&\n" - "bisect_nr=%d &&\n" - "bisect_good=%d &&\n" - "bisect_bad=%d &&\n" - "bisect_all=%d &&\n" - "bisect_steps=%d\n" - : - "bisect_rev=%s\n" - "bisect_nr=%d\n" - "bisect_good=%d\n" - "bisect_bad=%d\n" - "bisect_all=%d\n" - "bisect_steps=%d\n"; - printf(format, - hex, - cnt - 1, - all - reaches - 1, - reaches - 1, - all, - estimate_bisect_steps(all)); + show_tried_revs(tried); + + print_var_str("bisect_rev", hex); + print_var_int("bisect_nr", cnt - 1); + print_var_int("bisect_good", all - reaches - 1); + print_var_int("bisect_bad", reaches - 1); + print_var_int("bisect_all", all); + print_var_int("bisect_steps", estimate_bisect_steps(all)); return 0; } diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index 22c6d6ad16..112d622cda 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -318,7 +318,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) int onb = 0, osz = 0, unb = 0, usz = 0; strbuf_addstr(&parsed, "set --"); - argc = parse_options(argc, argv, parseopt_opts, parseopt_usage, + argc = parse_options(argc, argv, prefix, parseopt_opts, parseopt_usage, PARSE_OPT_KEEP_DASHDASH); if (argc < 1 || strcmp(argv[0], "--")) usage_with_options(parseopt_usage, parseopt_opts); @@ -393,7 +393,7 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) /* put an OPT_END() */ ALLOC_GROW(opts, onb + 1, osz); memset(opts + onb, 0, sizeof(opts[onb])); - argc = parse_options(argc, argv, opts, usage, + argc = parse_options(argc, argv, prefix, opts, usage, keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0); strbuf_addf(&parsed, " --"); @@ -402,6 +402,18 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix) return 0; } +static int cmd_sq_quote(int argc, const char **argv) +{ + struct strbuf buf = STRBUF_INIT; + + if (argc) + sq_quote_argv(&buf, argv, 0); + printf("%s\n", buf.buf); + strbuf_release(&buf); + + return 0; +} + static void die_no_single_rev(int quiet) { if (quiet) @@ -419,6 +431,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) if (argc > 1 && !strcmp("--parseopt", argv[1])) return cmd_parseopt(argc - 1, argv + 1, prefix); + if (argc > 1 && !strcmp("--sq-quote", argv[1])) + return cmd_sq_quote(argc - 2, argv + 2); + prefix = setup_git_directory(); git_config(git_default_config, NULL); for (i = 1; i < argc; i++) { diff --git a/builtin-revert.c b/builtin-revert.c index 3f2614e1bb..c87115af30 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -60,7 +60,7 @@ static void parse_args(int argc, const char **argv) OPT_END(), }; - if (parse_options(argc, argv, options, usage_str, 0) != 1) + if (parse_options(argc, argv, NULL, options, usage_str, 0) != 1) usage_with_options(usage_str, options); arg = argv[0]; @@ -323,9 +323,9 @@ static int revert_or_cherry_pick(int argc, const char **argv) encoding = get_encoding(message); if (!encoding) - encoding = "utf-8"; + encoding = "UTF-8"; if (!git_commit_encoding) - git_commit_encoding = "utf-8"; + git_commit_encoding = "UTF-8"; if ((reencoded_message = reencode_string(message, git_commit_encoding, encoding))) message = reencoded_message; diff --git a/builtin-rm.c b/builtin-rm.c index 269d60890a..0cc4912718 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -157,7 +157,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); - argc = parse_options(argc, argv, builtin_rm_options, builtin_rm_usage, 0); + argc = parse_options(argc, argv, prefix, builtin_rm_options, + builtin_rm_usage, 0); if (!argc) usage_with_options(builtin_rm_usage, builtin_rm_options); diff --git a/builtin-send-pack.c b/builtin-send-pack.c index 473a3de40c..c375a3dbde 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -178,9 +178,9 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, str { fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary); if (from) - fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to)); + fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name)); else - fputs(prettify_ref(to), stderr); + fputs(prettify_refname(to->name), stderr); if (msg) { fputs(" (", stderr); fputs(msg, stderr); @@ -473,7 +473,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) int fd[2]; struct child_process *conn; struct extra_have_objects extra_have; - struct ref *remote_refs, **remote_tail, *local_refs; + struct ref *remote_refs, *local_refs; int ret; int send_all = 0; const char *receivepack = "git-receive-pack"; @@ -567,13 +567,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) flags |= MATCH_REFS_MIRROR; /* match them up */ - remote_tail = &remote_refs; - while (*remote_tail) - remote_tail = &((*remote_tail)->next); - if (match_refs(local_refs, remote_refs, &remote_tail, - nr_refspecs, refspecs, flags)) { + if (match_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags)) return -1; - } ret = send_pack(&args, fd, conn, remote_refs, &extra_have); diff --git a/builtin-shortlog.c b/builtin-shortlog.c index b28091b445..6a3812ee18 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -263,7 +263,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) git_config(git_default_config, NULL); shortlog_init(&log); init_revisions(&rev, prefix); - parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH | + parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0); for (;;) { diff --git a/builtin-show-branch.c b/builtin-show-branch.c index c3afabbe91..01bea3b583 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -2,11 +2,26 @@ #include "commit.h" #include "refs.h" #include "builtin.h" +#include "color.h" +#include "parse-options.h" -static const char show_branch_usage[] = -"git show-branch [--sparse] [--current] [--all] [--remotes] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...] | --reflog[=n[,b]] <branch>"; -static const char show_branch_usage_reflog[] = -"--reflog is incompatible with --all, --remotes, --independent or --merge-base"; +static const char* show_branch_usage[] = { + "git show-branch [--sparse] [--current] [--all] [--remotes] [--topo-order] [--more=count | --list | --independent | --merge-base] [--topics] [--color] [<refs>...]", + "--reflog[=n[,b]] [--list] [--color] <branch>", + NULL +}; + +static int showbranch_use_color = -1; +static char column_colors[][COLOR_MAXLEN] = { + GIT_COLOR_RED, + GIT_COLOR_GREEN, + GIT_COLOR_YELLOW, + GIT_COLOR_BLUE, + GIT_COLOR_MAGENTA, + GIT_COLOR_CYAN, +}; + +#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors)) static int default_num; static int default_alloc; @@ -19,6 +34,20 @@ static const char **default_arg; #define DEFAULT_REFLOG 4 +static const char *get_color_code(int idx) +{ + if (showbranch_use_color) + return column_colors[idx]; + return ""; +} + +static const char *get_color_reset_code(void) +{ + if (showbranch_use_color) + return GIT_COLOR_RESET; + return ""; +} + static struct commit *interesting(struct commit_list *list) { while (list) { @@ -545,7 +574,12 @@ static int git_show_branch_config(const char *var, const char *value, void *cb) return 0; } - return git_default_config(var, value, cb); + if (!strcmp(var, "color.showbranch")) { + showbranch_use_color = git_config_colorbool(var, value, -1); + return 0; + } + + return git_color_default_config(var, value, cb); } static int omit_in_dense(struct commit *commit, struct commit **rev, int n) @@ -569,18 +603,25 @@ static int omit_in_dense(struct commit *commit, struct commit **rev, int n) return 0; } -static void parse_reflog_param(const char *arg, int *cnt, const char **base) +static int reflog = 0; + +static int parse_reflog_param(const struct option *opt, const char *arg, + int unset) { char *ep; - *cnt = strtoul(arg, &ep, 10); + const char **base = (const char **)opt->value; + if (!arg) + arg = ""; + reflog = strtoul(arg, &ep, 10); if (*ep == ',') *base = ep + 1; else if (*ep) - die("unrecognized reflog param '%s'", arg); + return error("unrecognized reflog param '%s'", arg); else *base = NULL; - if (*cnt <= 0) - *cnt = DEFAULT_REFLOG; + if (reflog <= 0) + reflog = DEFAULT_REFLOG; + return 0; } int cmd_show_branch(int ac, const char **av, const char *prefix) @@ -606,70 +647,67 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) int head_at = -1; int topics = 0; int dense = 1; - int reflog = 0; const char *reflog_base = NULL; + struct option builtin_show_branch_options[] = { + OPT_BOOLEAN('a', "all", &all_heads, + "show remote-tracking and local branches"), + OPT_BOOLEAN('r', "remotes", &all_remotes, + "show remote-tracking branches"), + OPT_BOOLEAN(0, "color", &showbranch_use_color, + "color '*!+-' corresponding to the branch"), + { OPTION_INTEGER, 0, "more", &extra, "n", + "show <n> more commits after the common ancestor", + PARSE_OPT_OPTARG, NULL, (intptr_t)1 }, + OPT_SET_INT(0, "list", &extra, "synonym to more=-1", -1), + OPT_BOOLEAN(0, "no-name", &no_name, "suppress naming strings"), + OPT_BOOLEAN(0, "current", &with_current_branch, + "include the current branch"), + OPT_BOOLEAN(0, "sha1-name", &sha1_name, + "name commits with their object names"), + OPT_BOOLEAN(0, "merge-base", &merge_base, + "act like git merge-base -a"), + OPT_BOOLEAN(0, "independent", &independent, + "show refs unreachable from any other ref"), + OPT_BOOLEAN(0, "topo-order", &lifo, + "show commits in topological order"), + OPT_BOOLEAN(0, "topics", &topics, + "show only commits not on the first branch"), + OPT_SET_INT(0, "sparse", &dense, + "show merges reachable from only one tip", 0), + OPT_SET_INT(0, "date-order", &lifo, + "show commits where no parent comes before its " + "children", 0), + { OPTION_CALLBACK, 'g', "reflog", &reflog_base, "<n>[,<base>]", + "show <n> most recent ref-log entries starting at " + "base", + PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, + parse_reflog_param }, + OPT_END() + }; git_config(git_show_branch_config, NULL); + if (showbranch_use_color == -1) + showbranch_use_color = git_use_color_default; + /* If nothing is specified, try the default first */ if (ac == 1 && default_num) { ac = default_num + 1; av = default_arg - 1; /* ick; we would not address av[0] */ } - while (1 < ac && av[1][0] == '-') { - const char *arg = av[1]; - if (!strcmp(arg, "--")) { - ac--; av++; - break; - } - else if (!strcmp(arg, "--all") || !strcmp(arg, "-a")) - all_heads = all_remotes = 1; - else if (!strcmp(arg, "--remotes") || !strcmp(arg, "-r")) - all_remotes = 1; - else if (!strcmp(arg, "--more")) - extra = 1; - else if (!strcmp(arg, "--list")) - extra = -1; - else if (!strcmp(arg, "--no-name")) - no_name = 1; - else if (!strcmp(arg, "--current")) - with_current_branch = 1; - else if (!strcmp(arg, "--sha1-name")) - sha1_name = 1; - else if (!prefixcmp(arg, "--more=")) - extra = atoi(arg + 7); - else if (!strcmp(arg, "--merge-base")) - merge_base = 1; - else if (!strcmp(arg, "--independent")) - independent = 1; - else if (!strcmp(arg, "--topo-order")) - lifo = 1; - else if (!strcmp(arg, "--topics")) - topics = 1; - else if (!strcmp(arg, "--sparse")) - dense = 0; - else if (!strcmp(arg, "--date-order")) - lifo = 0; - else if (!strcmp(arg, "--reflog") || !strcmp(arg, "-g")) { - reflog = DEFAULT_REFLOG; - } - else if (!prefixcmp(arg, "--reflog=")) - parse_reflog_param(arg + 9, &reflog, &reflog_base); - else if (!prefixcmp(arg, "-g=")) - parse_reflog_param(arg + 3, &reflog, &reflog_base); - else - usage(show_branch_usage); - ac--; av++; - } - ac--; av++; + ac = parse_options(ac, av, prefix, builtin_show_branch_options, + show_branch_usage, PARSE_OPT_STOP_AT_NON_OPTION); + if (all_heads) + all_remotes = 1; if (extra || reflog) { /* "listing" mode is incompatible with * independent nor merge-base modes. */ if (independent || merge_base) - usage(show_branch_usage); + usage_with_options(show_branch_usage, + builtin_show_branch_options); if (reflog && ((0 < extra) || all_heads || all_remotes)) /* * Asking for --more in reflog mode does not @@ -677,7 +715,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) * * Also --all and --remotes do not make sense either. */ - usage(show_branch_usage_reflog); + die("--reflog is incompatible with --all, --remotes, " + "--independent or --merge-base"); } /* If nothing is specified, show all branches by default */ @@ -843,8 +882,10 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) else { for (j = 0; j < i; j++) putchar(' '); - printf("%c [%s] ", - is_head ? '*' : '!', ref_name[i]); + printf("%s%c%s [%s] ", + get_color_code(i % COLUMN_COLORS_MAX), + is_head ? '*' : '!', + get_color_reset_code(), ref_name[i]); } if (!reflog) { @@ -903,7 +944,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) mark = '*'; else mark = '+'; - putchar(mark); + printf("%s%c%s", + get_color_code(i % COLUMN_COLORS_MAX), + mark, get_color_reset_code()); } putchar(' '); } diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index 6ae6bcc0e8..ca855a5eb2 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -36,7 +36,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) }; git_config(git_default_config, NULL); - argc = parse_options(argc, argv, options, git_symbolic_ref_usage, 0); + argc = parse_options(argc, argv, prefix, options, + git_symbolic_ref_usage, 0); if (msg &&!*msg) die("Refusing to perform update with empty message"); switch (argc) { diff --git a/builtin-tag.c b/builtin-tag.c index e544430094..dc3db62811 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -387,7 +387,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) "annotated tag, needs a message"), OPT_CALLBACK('m', NULL, &msg, "msg", "message for the tag", parse_msg_arg), - OPT_STRING('F', NULL, &msgfile, "file", "message in a file"), + OPT_FILENAME('F', NULL, &msgfile, "message in a file"), OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"), OPT_STRING('u', NULL, &keyid, "key-id", "use another key to sign the tag"), @@ -405,8 +405,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) git_config(git_tag_config, NULL); - argc = parse_options(argc, argv, options, git_tag_usage, 0); - msgfile = parse_options_fix_filename(prefix, msgfile); + argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0); if (keyid) { sign = 1; diff --git a/builtin-update-ref.c b/builtin-update-ref.c index 378dc1b7a6..76ba1d5881 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -23,7 +23,8 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) }; git_config(git_default_config, NULL); - argc = parse_options(argc, argv, options, git_update_ref_usage, 0); + argc = parse_options(argc, argv, prefix, options, git_update_ref_usage, + 0); if (msg && !*msg) die("Refusing to perform update with empty message."); @@ -72,6 +72,7 @@ extern int cmd_merge_base(int argc, const char **argv, const char *prefix); extern int cmd_merge_ours(int argc, const char **argv, const char *prefix); extern int cmd_merge_file(int argc, const char **argv, const char *prefix); extern int cmd_merge_recursive(int argc, const char **argv, const char *prefix); +extern int cmd_mktree(int argc, const char **argv, const char *prefix); extern int cmd_mv(int argc, const char **argv, const char *prefix); extern int cmd_name_rev(int argc, const char **argv, const char *prefix); extern int cmd_pack_objects(int argc, const char **argv, const char *prefix); @@ -98,7 +98,7 @@ int verify_bundle(struct bundle_header *header, int verbose) */ struct ref_list *p = &header->prerequisites; struct rev_info revs; - const char *argv[] = {NULL, "--all"}; + const char *argv[] = {NULL, "--all", NULL}; struct object_array refs; struct commit *commit; int i, ret = 0, req_nr; @@ -614,6 +614,8 @@ extern int is_empty_blob_sha1(const unsigned char *sha1); int git_mkstemp(char *path, size_t n, const char *template); +int git_mkstemps(char *path, size_t n, const char *template, int suffix_len); + /* * NOTE NOTE NOTE!! * @@ -316,6 +316,26 @@ int parse_commit(struct commit *item) return ret; } +static void unparse_commit_list(struct commit_list *list) +{ + for (; list; list = list->next) + unparse_commit(list->item); +} + +void unparse_commit(struct commit *item) +{ + item->object.flags = 0; + item->object.used = 0; + if (item->object.parsed) { + item->object.parsed = 0; + if (item->parents) { + unparse_commit_list(item->parents); + free_commit_list(item->parents); + item->parents = NULL; + } + } +} + struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p) { struct commit_list *new_list = xmalloc(sizeof(struct commit_list)); @@ -40,6 +40,8 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size); int parse_commit(struct commit *item); +void unparse_commit(struct commit *item); + struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p); unsigned commit_list_count(const struct commit_list *l); struct commit_list * insert_by_date(struct commit *item, struct commit_list **list); diff --git a/compat/basename.c b/compat/basename.c new file mode 100644 index 0000000000..d8f8a3c6dc --- /dev/null +++ b/compat/basename.c @@ -0,0 +1,15 @@ +#include "../git-compat-util.h" + +/* Adapted from libiberty's basename.c. */ +char *gitbasename (char *path) +{ + const char *base; + /* Skip over the disk name in MSDOS pathnames. */ + if (has_dos_drive_prefix(path)) + path += 2; + for (base = path; *path; path++) { + if (is_dir_sep(*path)) + base = path + 1; + } + return (char *)base; +} diff --git a/compat/mingw.c b/compat/mingw.c index cdeda1d985..e190fddf41 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -525,8 +525,8 @@ static const char *parse_interpreter(const char *cmd) if (buf[0] != '#' || buf[1] != '!') return NULL; buf[n] = '\0'; - p = strchr(buf, '\n'); - if (!p) + p = buf + strcspn(buf, "\r\n"); + if (!*p) return NULL; *p = '\0'; @@ -1156,3 +1156,18 @@ int link(const char *oldpath, const char *newpath) } return 0; } + +char *getpass(const char *prompt) +{ + struct strbuf buf = STRBUF_INIT; + + fputs(prompt, stderr); + for (;;) { + char c = _getch(); + if (c == '\r' || c == '\n') + break; + strbuf_addch(&buf, c); + } + fputs("\n", stderr); + return strbuf_detach(&buf, NULL); +} diff --git a/compat/mingw.h b/compat/mingw.h index 762eb143a7..4c50f5b1bc 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -38,6 +38,8 @@ struct passwd { char *pw_dir; }; +extern char *getpass(const char *prompt); + struct pollfd { int fd; /* file descriptor */ short events; /* requested events */ @@ -109,7 +111,7 @@ static inline int mingw_unlink(const char *pathname) } #define unlink mingw_unlink -static inline int waitpid(pid_t pid, unsigned *status, unsigned options) +static inline int waitpid(pid_t pid, int *status, unsigned options) { if (options == 0) return _cwait(status, pid, 0); diff --git a/compat/mkstemps.c b/compat/mkstemps.c new file mode 100644 index 0000000000..14179c8e6d --- /dev/null +++ b/compat/mkstemps.c @@ -0,0 +1,70 @@ +#include "../git-compat-util.h" + +/* Adapted from libiberty's mkstemp.c. */ + +#undef TMP_MAX +#define TMP_MAX 16384 + +int gitmkstemps(char *pattern, int suffix_len) +{ + static const char letters[] = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"; + static const int num_letters = 62; + uint64_t value; + struct timeval tv; + char *template; + size_t len; + int fd, count; + + len = strlen(pattern); + + if (len < 6 + suffix_len) { + errno = EINVAL; + return -1; + } + + if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) { + errno = EINVAL; + return -1; + } + + /* + * Replace pattern's XXXXXX characters with randomness. + * Try TMP_MAX different filenames. + */ + gettimeofday(&tv, NULL); + value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid(); + template = &pattern[len - 6 - suffix_len]; + for (count = 0; count < TMP_MAX; ++count) { + uint64_t v = value; + /* Fill in the random bits. */ + template[0] = letters[v % num_letters]; v /= num_letters; + template[1] = letters[v % num_letters]; v /= num_letters; + template[2] = letters[v % num_letters]; v /= num_letters; + template[3] = letters[v % num_letters]; v /= num_letters; + template[4] = letters[v % num_letters]; v /= num_letters; + template[5] = letters[v % num_letters]; v /= num_letters; + + fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, 0600); + if (fd > 0) + return fd; + /* + * Fatal error (EPERM, ENOSPC etc). + * It doesn't make sense to loop. + */ + if (errno != EEXIST) + break; + /* + * This is a random value. It is only necessary that + * the next TMP_MAX values generated by adding 7777 to + * VALUE are different with (module 2^32). + */ + value += 7777; + } + /* We return the null string if we can't find a unique file name. */ + pattern[0] = '\0'; + errno = EINVAL; + return -1; +} diff --git a/config.mak.in b/config.mak.in index 7cce0c12d5..dd60451318 100644 --- a/config.mak.in +++ b/config.mak.in @@ -30,8 +30,10 @@ NEEDS_SSL_WITH_CRYPTO=@NEEDS_SSL_WITH_CRYPTO@ NO_OPENSSL=@NO_OPENSSL@ NO_CURL=@NO_CURL@ NO_EXPAT=@NO_EXPAT@ +NO_LIBGEN_H=@NO_LIBGEN_H@ NEEDS_LIBICONV=@NEEDS_LIBICONV@ NEEDS_SOCKET=@NEEDS_SOCKET@ +NEEDS_RESOLV=@NEEDS_RESOLV@ NO_SYS_SELECT_H=@NO_SYS_SELECT_H@ NO_D_INO_IN_DIRENT=@NO_D_INO_IN_DIRENT@ NO_D_TYPE_IN_DIRENT=@NO_D_TYPE_IN_DIRENT@ @@ -46,6 +48,7 @@ NO_STRTOUMAX=@NO_STRTOUMAX@ NO_SETENV=@NO_SETENV@ NO_UNSETENV=@NO_UNSETENV@ NO_MKDTEMP=@NO_MKDTEMP@ +NO_MKSTEMPS=@NO_MKSTEMPS@ NO_ICONV=@NO_ICONV@ OLD_ICONV=@OLD_ICONV@ NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@ diff --git a/configure.ac b/configure.ac index 4e728bca35..7937e604be 100644 --- a/configure.ac +++ b/configure.ac @@ -467,6 +467,15 @@ AC_CHECK_LIB([c], [socket], AC_SUBST(NEEDS_SOCKET) test -n "$NEEDS_SOCKET" && LIBS="$LIBS -lsocket" +# +# Define NEEDS_RESOLV if linking with -lnsl and/or -lsocket is not enough. +# Notably on Solaris hstrerror resides in libresolv and on Solaris 7 +# inet_ntop and inet_pton additionally reside there. +AC_CHECK_LIB([resolv], [hstrerror], +[NEEDS_RESOLV=], +[NEEDS_RESOLV=YesPlease]) +AC_SUBST(NEEDS_RESOLV) +test -n "$NEEDS_RESOLV" && LIBS="$LIBS -lresolv" ## Checks for header files. AC_MSG_NOTICE([CHECKS for header files]) @@ -627,6 +636,12 @@ AC_SUBST(SNPRINTF_RETURNS_BOGUS) ## (in default C library and libraries checked by AC_CHECK_LIB) AC_MSG_NOTICE([CHECKS for library functions]) # +# Define NO_LIBGEN_H if you don't have libgen.h. +AC_CHECK_HEADER([libgen.h], +[NO_LIBGEN_H=], +[NO_LIBGEN_H=YesPlease]) +AC_SUBST(NO_LIBGEN_H) +# # Define NO_STRCASESTR if you don't have strcasestr. GIT_CHECK_FUNC(strcasestr, [NO_STRCASESTR=], @@ -677,6 +692,13 @@ GIT_CHECK_FUNC(mkdtemp, [NO_MKDTEMP=YesPlease]) AC_SUBST(NO_MKDTEMP) # +# Define NO_MKSTEMPS if you don't have mkstemps in the C library. +GIT_CHECK_FUNC(mkstemps, +[NO_MKSTEMPS=], +[NO_MKSTEMPS=YesPlease]) +AC_SUBST(NO_MKSTEMPS) +# +# # Define NO_MMAP if you want to avoid mmap. # # Define NO_ICONV if your libc does not properly support iconv. @@ -579,7 +579,10 @@ struct child_process *git_connect(int fd[2], const char *url_orig, git_tcp_connect(fd, host, flags); /* * Separate original protocol components prog and path - * from extended components with a NUL byte. + * from extended host header with a NUL byte. + * + * Note: Do not add any other headers here! Doing so + * will cause older git-daemon servers to crash. */ packet_write(fd[1], "%s %s%chost=%s%c", diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f44152c433..80ab4e45eb 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -40,6 +40,10 @@ # with the bash.showDirtyState variable, which defaults to true # once GIT_PS1_SHOWDIRTYSTATE is enabled. # +# You can also see if currently something is stashed, by setting +# GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed, +# then a '$' will be shown next to the branch name. +# # To submit patches: # # *) Read Documentation/SubmittingPatches @@ -84,26 +88,24 @@ __git_ps1 () if [ -n "$g" ]; then local r local b - if [ -d "$g/rebase-apply" ]; then - if [ -f "$g/rebase-apply/rebasing" ]; then - r="|REBASE" - elif [ -f "$g/rebase-apply/applying" ]; then - r="|AM" - else - r="|AM/REBASE" - fi - b="$(git symbolic-ref HEAD 2>/dev/null)" - elif [ -f "$g/rebase-merge/interactive" ]; then + if [ -f "$g/rebase-merge/interactive" ]; then r="|REBASE-i" b="$(cat "$g/rebase-merge/head-name")" elif [ -d "$g/rebase-merge" ]; then r="|REBASE-m" b="$(cat "$g/rebase-merge/head-name")" else - if [ -f "$g/MERGE_HEAD" ]; then + if [ -d "$g/rebase-apply" ]; then + if [ -f "$g/rebase-apply/rebasing" ]; then + r="|REBASE" + elif [ -f "$g/rebase-apply/applying" ]; then + r="|AM" + else + r="|AM/REBASE" + fi + elif [ -f "$g/MERGE_HEAD" ]; then r="|MERGING" - fi - if [ -f "$g/BISECT_LOG" ]; then + elif [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi @@ -129,6 +131,7 @@ __git_ps1 () local w local i + local s local c if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then @@ -150,14 +153,15 @@ __git_ps1 () fi fi fi + if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then + git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$" + fi fi - if [ -n "$b" ]; then - if [ -n "${1-}" ]; then - printf "$1" "$c${b##refs/heads/}$w$i$r" - else - printf " (%s)" "$c${b##refs/heads/}$w$i$r" - fi + if [ -n "${1-}" ]; then + printf "$1" "$c${b##refs/heads/}$w$i$s$r" + else + printf " (%s)" "$c${b##refs/heads/}$w$i$s$r" fi fi } @@ -923,7 +927,7 @@ _git_diff () } __git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff - tkdiff vimdiff gvimdiff xxdiff + tkdiff vimdiff gvimdiff xxdiff araxis " _git_difftool () @@ -1334,6 +1338,35 @@ _git_send_email () COMPREPLY=() } +__git_config_get_set_variables () +{ + local prevword word config_file= c=$COMP_CWORD + while [ $c -gt 1 ]; do + word="${COMP_WORDS[c]}" + case "$word" in + --global|--system|--file=*) + config_file="$word" + break + ;; + -f|--file) + config_file="$word $prevword" + break + ;; + esac + prevword=$word + c=$((--c)) + done + + for i in $(git --git-dir="$(__gitdir)" config $config_file --list \ + 2>/dev/null); do + case "$i" in + *.*) + echo "${i/=*/}" + ;; + esac + done +} + _git_config () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1365,7 +1398,8 @@ _git_config () __gitcomp "$(__git_merge_strategies)" return ;; - color.branch|color.diff|color.interactive|color.status|color.ui) + color.branch|color.diff|color.interactive|\ + color.showbranch|color.status|color.ui) __gitcomp "always never auto" return ;; @@ -1400,6 +1434,10 @@ _git_config () __gitcomp "$__git_send_email_suppresscc_options" return ;; + --get|--get-all|--unset|--unset-all) + __gitcomp "$(__git_config_get_set_variables)" + return + ;; *.*) COMPREPLY=() return @@ -1510,6 +1548,7 @@ _git_config () color.interactive.help color.interactive.prompt color.pager + color.showbranch color.status color.status.added color.status.changed @@ -1821,6 +1860,7 @@ _git_show_branch () __gitcomp " --all --remotes --topo-order --current --more= --list --independent --merge-base --no-name + --color --no-color --sha1-name --sparse --topics --reflog " return @@ -1841,10 +1881,10 @@ _git_stash () save,--*) __gitcomp "--keep-index" ;; - apply,--*) + apply,--*|pop,--*) __gitcomp "--index" ;; - show,--*|drop,--*|pop,--*|branch,--*) + show,--*|drop,--*|branch,--*) COMPREPLY=() ;; show,*|apply,*|drop,*|pop,*|branch,*) diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 60cbab65d3..2a66063e44 100644..100755 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -44,6 +44,10 @@ # --pretty %s", displaying the commit id, author, date and log # message. To list full patches separated by a blank line, you # could set this to "git show -C %s; echo". +# To list a gitweb/cgit URL *and* a full patch for each change set, use this: +# "t=%s; printf 'http://.../?id=%%s' \$t; echo;echo; git show -C \$t; echo" +# Be careful if "..." contains things that will be expanded by shell "eval" +# or printf. # # Notes # ----- @@ -406,15 +406,15 @@ static char *xstrdup_tolower(const char *str) } /* - * Separate the "extra args" information as supplied by the client connection. + * Read the host as supplied by the client connection. */ -static void parse_extra_args(char *extra_args, int buflen) +static void parse_host_arg(char *extra_args, int buflen) { char *val; int vallen; char *end = extra_args + buflen; - while (extra_args < end && *extra_args) { + if (extra_args < end && *extra_args) { saw_extended_args = 1; if (strncasecmp("host=", extra_args, 5) == 0) { val = extra_args + 5; @@ -436,6 +436,8 @@ static void parse_extra_args(char *extra_args, int buflen) /* On to the next one */ extra_args = val + vallen; } + if (extra_args < end && *extra_args) + die("Invalid request"); } /* @@ -545,7 +547,7 @@ static int execute(struct sockaddr *addr) hostname = canon_hostname = ip_address = tcp_port = NULL; if (len != pktlen) - parse_extra_args(line + len + 1, pktlen - len - 1); + parse_host_arg(line + len + 1, pktlen - len - 1); for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { struct daemon_service *s = &(daemon_service[i]); diff --git a/decorate.c b/decorate.c index e6fd8a7441..2f8a63e388 100644 --- a/decorate.c +++ b/decorate.c @@ -18,7 +18,7 @@ static void *insert_decoration(struct decoration *n, const struct object *base, { int size = n->size; struct object_decoration *hash = n->hash; - int j = hash_obj(base, size); + unsigned int j = hash_obj(base, size); while (hash[j].base) { if (hash[j].base == base) { @@ -70,7 +70,7 @@ void *add_decoration(struct decoration *n, const struct object *obj, /* Lookup a decoration pointer */ void *lookup_decoration(struct decoration *n, const struct object *obj) { - int j; + unsigned int j; /* nothing to lookup */ if (!n->size) @@ -839,10 +839,9 @@ static int scale_linear(int it, int width, int max_change) } static void show_name(FILE *file, - const char *prefix, const char *name, int len, - const char *reset, const char *set) + const char *prefix, const char *name, int len) { - fprintf(file, " %s%s%-*s%s |", set, prefix, len, name, reset); + fprintf(file, " %s%-*s |", prefix, len, name); } static void show_graph(FILE *file, char ch, int cnt, const char *set, const char *reset) @@ -956,7 +955,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) } if (data->files[i]->is_binary) { - show_name(options->file, prefix, name, len, reset, set); + show_name(options->file, prefix, name, len); fprintf(options->file, " Bin "); fprintf(options->file, "%s%d%s", del_c, deleted, reset); fprintf(options->file, " -> "); @@ -966,7 +965,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) continue; } else if (data->files[i]->is_unmerged) { - show_name(options->file, prefix, name, len, reset, set); + show_name(options->file, prefix, name, len); fprintf(options->file, " Unmerged\n"); continue; } @@ -988,7 +987,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) add = scale_linear(add, width, max_change); del = scale_linear(del, width, max_change); } - show_name(options->file, prefix, name, len, reset, set); + show_name(options->file, prefix, name, len); fprintf(options->file, "%5d%s", added + deleted, added + deleted ? " " : ""); show_graph(options->file, '+', add, add_c, reset); @@ -996,8 +995,8 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) fprintf(options->file, "\n"); } fprintf(options->file, - "%s %d files changed, %d insertions(+), %d deletions(-)%s\n", - set, total_files, adds, dels, reset); + " %d files changed, %d insertions(+), %d deletions(-)\n", + total_files, adds, dels); } static void show_shortstats(struct diffstat_t* data, struct diff_options *options) @@ -1965,8 +1964,16 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp, { int fd; struct strbuf buf = STRBUF_INIT; + struct strbuf template = STRBUF_INIT; + char *path_dup = xstrdup(path); + const char *base = basename(path_dup); + + /* Generate "XXXXXX_basename.ext" */ + strbuf_addstr(&template, "XXXXXX_"); + strbuf_addstr(&template, base); - fd = git_mkstemp(temp->tmp_path, PATH_MAX, ".diff_XXXXXX"); + fd = git_mkstemps(temp->tmp_path, PATH_MAX, template.buf, + strlen(base) + 1); if (fd < 0) die("unable to create temp-file: %s", strerror(errno)); if (convert_to_working_tree(path, @@ -1982,6 +1989,8 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp, temp->hex[40] = 0; sprintf(temp->mode, "%06o", mode); strbuf_release(&buf); + strbuf_release(&template); + free(path_dup); } static struct diff_tempfile *prepare_temp_file(const char *name, @@ -2015,18 +2024,15 @@ static struct diff_tempfile *prepare_temp_file(const char *name, die("stat(%s): %s", name, strerror(errno)); } if (S_ISLNK(st.st_mode)) { - int ret; - char buf[PATH_MAX + 1]; /* ought to be SYMLINK_MAX */ - ret = readlink(name, buf, sizeof(buf)); - if (ret < 0) + struct strbuf sb = STRBUF_INIT; + if (strbuf_readlink(&sb, name, st.st_size) < 0) die("readlink(%s)", name); - if (ret == sizeof(buf)) - die("symlink too long: %s", name); - prep_temp_blob(name, temp, buf, ret, + prep_temp_blob(name, temp, sb.buf, sb.len, (one->sha1_valid ? one->sha1 : null_sha1), (one->sha1_valid ? one->mode : S_IFLNK)); + strbuf_release(&sb); } else { /* we can borrow from the file in the work tree */ @@ -3590,6 +3596,7 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec, if (start_command(&child) != 0 || strbuf_read(&buf, child.out, 0) < 0 || finish_command(&child) != 0) { + strbuf_release(&buf); remove_tempfile(); error("error running textconv command '%s'", pgm); return NULL; @@ -396,7 +396,7 @@ static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathna static struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len) { - if (cache_name_pos(pathname, len) >= 0) + if (!cache_name_is_other(pathname, len)) return NULL; ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->ignored_alloc); diff --git a/git-add--interactive.perl b/git-add--interactive.perl index f6e536ece3..df9f231635 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -767,6 +767,96 @@ sub split_hunk { return @split; } +sub find_last_o_ctx { + my ($it) = @_; + my $text = $it->{TEXT}; + my ($o_ofs, $o_cnt) = parse_hunk_header($text->[0]); + my $i = @{$text}; + my $last_o_ctx = $o_ofs + $o_cnt; + while (0 < --$i) { + my $line = $text->[$i]; + if ($line =~ /^ /) { + $last_o_ctx--; + next; + } + last; + } + return $last_o_ctx; +} + +sub merge_hunk { + my ($prev, $this) = @_; + my ($o0_ofs, $o0_cnt, $n0_ofs, $n0_cnt) = + parse_hunk_header($prev->{TEXT}[0]); + my ($o1_ofs, $o1_cnt, $n1_ofs, $n1_cnt) = + parse_hunk_header($this->{TEXT}[0]); + + my (@line, $i, $ofs, $o_cnt, $n_cnt); + $ofs = $o0_ofs; + $o_cnt = $n_cnt = 0; + for ($i = 1; $i < @{$prev->{TEXT}}; $i++) { + my $line = $prev->{TEXT}[$i]; + if ($line =~ /^\+/) { + $n_cnt++; + push @line, $line; + next; + } + + last if ($o1_ofs <= $ofs); + + $o_cnt++; + $ofs++; + if ($line =~ /^ /) { + $n_cnt++; + } + push @line, $line; + } + + for ($i = 1; $i < @{$this->{TEXT}}; $i++) { + my $line = $this->{TEXT}[$i]; + if ($line =~ /^\+/) { + $n_cnt++; + push @line, $line; + next; + } + $ofs++; + $o_cnt++; + if ($line =~ /^ /) { + $n_cnt++; + } + push @line, $line; + } + my $head = ("@@ -$o0_ofs" . + (($o_cnt != 1) ? ",$o_cnt" : '') . + " +$n0_ofs" . + (($n_cnt != 1) ? ",$n_cnt" : '') . + " @@\n"); + @{$prev->{TEXT}} = ($head, @line); +} + +sub coalesce_overlapping_hunks { + my (@in) = @_; + my @out = (); + + my ($last_o_ctx, $last_was_dirty); + + for (grep { $_->{USE} } @in) { + my $text = $_->{TEXT}; + my ($o_ofs) = parse_hunk_header($text->[0]); + if (defined $last_o_ctx && + $o_ofs <= $last_o_ctx && + !$_->{DIRTY} && + !$last_was_dirty) { + merge_hunk($out[-1], $_); + } + else { + push @out, $_; + } + $last_o_ctx = find_last_o_ctx($out[-1]); + $last_was_dirty = $_->{DIRTY}; + } + return @out; +} sub color_diff { return map { @@ -878,7 +968,8 @@ sub edit_hunk_loop { my $newhunk = { TEXT => $text, TYPE => $hunk->[$ix]->{TYPE}, - USE => 1 + USE => 1, + DIRTY => 1, }; if (diff_applies($head, @{$hunk}[0..$ix-1], @@ -1210,6 +1301,8 @@ sub patch_update_file { } } + @hunk = coalesce_overlapping_hunks(@hunk); + my $n_lofs = 0; my @result = (); for (@hunk) { @@ -44,11 +44,7 @@ else fi sq () { - for sqarg - do - printf "%s" "$sqarg" | - sed -e 's/'\''/'\''\\'\'''\''/g' -e 's/.*/ '\''&'\''/' - done + git rev-parse --sq-quote "$@" } stop_here () { diff --git a/git-bisect.sh b/git-bisect.sh index 24712ff304..8969553658 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -33,16 +33,6 @@ require_work_tree _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" -sq() { - @@PERL@@ -e ' - for (@ARGV) { - s/'\''/'\'\\\\\'\''/g; - print " '\''$_'\''"; - } - print "\n"; - ' "$@" -} - bisect_autostart() { test -s "$GIT_DIR/BISECT_START" || { echo >&2 'You need to start by "git bisect start"' @@ -107,7 +97,7 @@ bisect_start() { for arg; do case "$arg" in --) has_double_dash=1; break ;; esac done - orig_args=$(sq "$@") + orig_args=$(git rev-parse --sq-quote "$@") bad_seen=0 eval='' while [ $# -gt 0 ]; do @@ -147,7 +137,7 @@ bisect_start() { # Write new start state. # echo "$start_head" >"$GIT_DIR/BISECT_START" && - sq "$@" >"$GIT_DIR/BISECT_NAMES" && + git rev-parse --sq-quote "$@" >"$GIT_DIR/BISECT_NAMES" && eval "$eval" && echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit # @@ -177,10 +167,6 @@ is_expected_rev() { test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV") } -mark_expected_rev() { - echo "$1" > "$GIT_DIR/BISECT_EXPECTED_REV" -} - check_expected_revs() { for _rev in "$@"; do if ! is_expected_rev "$_rev"; then @@ -199,7 +185,7 @@ bisect_skip() { *..*) revs=$(git rev-list "$arg") || die "Bad rev input: $arg" ;; *) - revs=$(sq "$arg") ;; + revs=$(git rev-parse --sq-quote "$arg") ;; esac all="$all $revs" done @@ -279,162 +265,22 @@ bisect_auto_next() { bisect_next_check && bisect_next || : } -exit_if_skipped_commits () { - _tried=$1 - _bad=$2 - if test -n "$_tried" ; then - echo "There are only 'skip'ped commit left to test." - echo "The first bad commit could be any of:" - echo "$_tried" | tr '[|]' '[\012]' - test -n "$_bad" && echo "$_bad" - echo "We cannot bisect more!" - exit 2 - fi -} - -bisect_checkout() { - _rev="$1" - _msg="$2" - echo "Bisecting: $_msg" - mark_expected_rev "$_rev" - git checkout -q "$_rev" -- || exit - git show-branch "$_rev" -} - -is_among() { - _rev="$1" - _list="$2" - case "$_list" in *$_rev*) return 0 ;; esac - return 1 -} - -handle_bad_merge_base() { - _badmb="$1" - _good="$2" - if is_expected_rev "$_badmb"; then - cat >&2 <<EOF -The merge base $_badmb is bad. -This means the bug has been fixed between $_badmb and [$_good]. -EOF - exit 3 - else - cat >&2 <<EOF -Some good revs are not ancestor of the bad rev. -git bisect cannot work properly in this case. -Maybe you mistake good and bad revs? -EOF - exit 1 - fi -} - -handle_skipped_merge_base() { - _mb="$1" - _bad="$2" - _good="$3" - cat >&2 <<EOF -Warning: the merge base between $_bad and [$_good] must be skipped. -So we cannot be sure the first bad commit is between $_mb and $_bad. -We continue anyway. -EOF -} - -# -# "check_merge_bases" checks that merge bases are not "bad". -# -# - If one is "good", that's good, we have nothing to do. -# - If one is "bad", it means the user assumed something wrong -# and we must exit. -# - If one is "skipped", we can't know but we should warn. -# - If we don't know, we should check it out and ask the user to test. -# -# In the last case we will return 1, and otherwise 0. -# -check_merge_bases() { - _bad="$1" - _good="$2" - _skip="$3" - for _mb in $(git merge-base --all $_bad $_good) - do - if is_among "$_mb" "$_good"; then - continue - elif test "$_mb" = "$_bad"; then - handle_bad_merge_base "$_bad" "$_good" - elif is_among "$_mb" "$_skip"; then - handle_skipped_merge_base "$_mb" "$_bad" "$_good" - else - bisect_checkout "$_mb" "a merge base must be tested" - return 1 - fi - done - return 0 -} - -# -# "check_good_are_ancestors_of_bad" checks that all "good" revs are -# ancestor of the "bad" rev. -# -# If that's not the case, we need to check the merge bases. -# If a merge base must be tested by the user we return 1 and -# otherwise 0. -# -check_good_are_ancestors_of_bad() { - test -f "$GIT_DIR/BISECT_ANCESTORS_OK" && - return - - _bad="$1" - _good=$(echo $2 | sed -e 's/\^//g') - _skip="$3" - - # Bisecting with no good rev is ok - test -z "$_good" && return - - _side=$(git rev-list $_good ^$_bad) - if test -n "$_side"; then - # Return if a checkout was done - check_merge_bases "$_bad" "$_good" "$_skip" || return - fi - - : > "$GIT_DIR/BISECT_ANCESTORS_OK" - - return 0 -} - bisect_next() { case "$#" in 0) ;; *) usage ;; esac bisect_autostart bisect_next_check good - # Get bad, good and skipped revs - bad=$(git rev-parse --verify refs/bisect/bad) && - good=$(git for-each-ref --format='^%(objectname)' \ - "refs/bisect/good-*" | tr '\012' ' ') && - skip=$(git for-each-ref --format='%(objectname)' \ - "refs/bisect/skip-*" | tr '\012' ' ') || exit - - # Maybe some merge bases must be tested first - check_good_are_ancestors_of_bad "$bad" "$good" "$skip" - # Return now if a checkout has already been done - test "$?" -eq "1" && return - - # Get bisection information - eval=$(eval "git bisect--helper --next-vars") && - eval "$eval" || exit - - if [ -z "$bisect_rev" ]; then - # We should exit here only if the "bad" - # commit is also a "skip" commit (see above). - exit_if_skipped_commits "$bisect_tried" - echo "$bad was both good and bad" - exit 1 - fi - if [ "$bisect_rev" = "$bad" ]; then - exit_if_skipped_commits "$bisect_tried" "$bad" - echo "$bisect_rev is first bad commit" - git diff-tree --pretty $bisect_rev - exit 0 - fi + # Perform all bisection computation, display and checkout + git bisect--helper --next-all + res=$? + + # Check if we should exit because bisection is finished + test $res -eq 10 && exit 0 - bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this (roughly $bisect_steps steps)" + # Check for an error in the bisection process + test $res -ne 0 && exit $res + + return 0 } bisect_visualize() { diff --git a/git-compat-util.h b/git-compat-util.h index c7cf2d5d9c..919b7f1ade 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -7,7 +7,7 @@ /* * See if our compiler is known to support flexible array members. */ -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && (!defined(__SUNPRO_C) || (__SUNPRO_C > 0x580)) # define FLEX_ARRAY /* empty */ #elif defined(__GNUC__) # if (__GNUC__ >= 3) @@ -39,10 +39,25 @@ /* Approximation of the length of the decimal representation of this type. */ #define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) -#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && !defined(_M_UNIX) +#if defined(__sun__) + /* + * On Solaris, when _XOPEN_EXTENDED is set, its header file + * forces the programs to be XPG4v2, defeating any _XOPEN_SOURCE + * setting to say we are XPG5 or XPG6. Also on Solaris, + * XPG6 programs must be compiled with a c99 compiler, while + * non XPG6 programs must be compiled with a pre-c99 compiler. + */ +# if __STDC_VERSION__ - 0 >= 199901L +# define _XOPEN_SOURCE 600 +# else +# define _XOPEN_SOURCE 500 +# endif +#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && !defined(_M_UNIX) #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */ +#ifndef __sun__ #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */ #endif +#endif #define _ALL_SOURCE 1 #define _GNU_SOURCE 1 #define _BSD_SOURCE 1 @@ -97,6 +112,13 @@ #include "compat/mingw.h" #endif /* __MINGW32__ */ +#ifndef NO_LIBGEN_H +#include <libgen.h> +#else +#define basename gitbasename +extern char *gitbasename(char *); +#endif + #ifndef NO_ICONV #include <iconv.h> #endif @@ -232,6 +254,11 @@ extern int gitsetenv(const char *, const char *, int); extern char *gitmkdtemp(char *); #endif +#ifdef NO_MKSTEMPS +#define mkstemps gitmkstemps +extern int gitmkstemps(char *, int); +#endif + #ifdef NO_UNSETENV #define unsetenv gitunsetenv extern void gitunsetenv(const char *); diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index 6d9f0ef0f9..9ec1df95c0 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -225,7 +225,14 @@ if (@canstatusfiles) { foreach my $name (keys %todo) { my $basename = basename($name); - $basename = "no file " . $basename if (exists($added{$basename})); + # CVS reports files that don't exist in the current revision as + # "no file $basename" in its "status" output, so we should + # anticipate that. Totally unknown files will have a status + # "Unknown". However, if they exist in the Attic, their status + # will be "Up-to-date" (this means they were added once but have + # been removed). + $basename = "no file $basename" if $added{$basename}; + $basename =~ s/^\s+//; $basename =~ s/\s+$//; @@ -233,31 +240,45 @@ if (@canstatusfiles) { $fullname{$basename} = $name; push (@canstatusfiles2, $name); delete($todo{$name}); - } + } } my @cvsoutput; @cvsoutput = xargs_safe_pipe_capture([@cvs, 'status'], @canstatusfiles2); foreach my $l (@cvsoutput) { - chomp $l; - if ($l =~ /^File:\s+(.*\S)\s+Status: (.*)$/) { - if (!exists($fullname{$1})) { - print STDERR "Huh? Status reported for unexpected file '$1'\n"; - } else { - $cvsstat{$fullname{$1}} = $2; - } - } + chomp $l; + next unless + my ($file, $status) = $l =~ /^File:\s+(.*\S)\s+Status: (.*)$/; + + my $fullname = $fullname{$file}; + print STDERR "Huh? Status '$status' reported for unexpected file '$file'\n" + unless defined $fullname; + + # This response means the file does not exist except in + # CVS's attic, so set the status accordingly + $status = "In-attic" + if $file =~ /^no file / + && $status eq 'Up-to-date'; + + $cvsstat{$fullname{$file}} = $status; } } } -# ... validate new files, +# ... Validate that new files have the correct status foreach my $f (@afiles) { - if (defined ($cvsstat{$f}) and $cvsstat{$f} ne "Unknown") { - $dirty = 1; + next unless defined(my $stat = $cvsstat{$f}); + + # This means the file has never been seen before + next if $stat eq 'Unknown'; + + # This means the file has been seen before but was removed + next if $stat eq 'In-attic'; + + $dirty = 1; warn "File $f is already known in your CVS checkout -- perhaps it has been added by another user. Or this may indicate that it exists on a different branch. If this is the case, use -f to force the merge.\n"; warn "Status was: $cvsstat{$f}\n"; - } } + # ... validate known files. foreach my $f (@files) { next if grep { $_ eq $f } @afiles; diff --git a/git-mergetool--lib.sh b/git-mergetool--lib.sh index a16a2795d7..bfb01f7842 100644 --- a/git-mergetool--lib.sh +++ b/git-mergetool--lib.sh @@ -18,6 +18,9 @@ translate_merge_tool_path () { emerge) echo emacs ;; + araxis) + echo compare + ;; *) echo "$1" ;; @@ -43,7 +46,7 @@ check_unchanged () { valid_tool () { case "$1" in kdiff3 | tkdiff | xxdiff | meld | opendiff | \ - emerge | vimdiff | gvimdiff | ecmerge | diffuse) + emerge | vimdiff | gvimdiff | ecmerge | diffuse | araxis) ;; # happy tortoisemerge) if ! merge_mode; then @@ -228,8 +231,8 @@ run_merge_tool () { fi check_unchanged else - "$merge_tool_path" "$LOCAL" "$REMOTE" \ - --default --mode=merge2 --to="$MERGED" + "$merge_tool_path" --default --mode=diff2 \ + "$LOCAL" "$REMOTE" fi ;; emerge) @@ -248,7 +251,7 @@ run_merge_tool () { status=$? else "$merge_tool_path" -f emerge-files-command \ - "$LOCAL" "$REMOTE" "$(basename "$MERGED")" + "$LOCAL" "$REMOTE" fi ;; tortoisemerge) @@ -263,6 +266,24 @@ run_merge_tool () { status=1 fi ;; + araxis) + if merge_mode; then + touch "$BACKUP" + if $base_present; then + "$merge_tool_path" -wait -merge -3 -a1 \ + "$BASE" "$LOCAL" "$REMOTE" "$MERGED" \ + >/dev/null 2>&1 + else + "$merge_tool_path" -wait -2 \ + "$LOCAL" "$REMOTE" "$MERGED" \ + >/dev/null 2>&1 + fi + check_unchanged + else + "$merge_tool_path" -wait -2 "$LOCAL" "$REMOTE" \ + >/dev/null 2>&1 + fi + ;; *) merge_tool_cmd="$(get_merge_tool_cmd "$1")" if test -z "$merge_tool_cmd"; then @@ -302,7 +323,7 @@ guess_merge_tool () { else tools="opendiff kdiff3 tkdiff xxdiff meld $tools" fi - tools="$tools gvimdiff diffuse ecmerge" + tools="$tools gvimdiff diffuse ecmerge araxis" fi if echo "${VISUAL:-$EDITOR}" | grep emacs > /dev/null 2>&1; then # $EDITOR is emacs so add emerge as a candidate diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 314cd364b8..f96d887d23 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -420,7 +420,7 @@ do_next () { NEWHEAD=$(git rev-parse HEAD) && case $HEADNAME in refs/*) - message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" && + message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO" && git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD && git symbolic-ref HEAD $HEADNAME ;; diff --git a/git-send-email.perl b/git-send-email.perl index cccbf4517a..4c795a4b03 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -210,6 +210,7 @@ my %config_settings = ( "envelopesender" => \$envelope_sender, "multiedit" => \$multiedit, "confirm" => \$confirm, + "from" => \$sender, ); # Handle Uncouth Termination @@ -409,7 +410,7 @@ my %parse_alias = ( mailrc => sub { my $fh = shift; while (<$fh>) { if (/^alias\s+(\S+)\s+(.*)$/) { # spaces delimit multiple addresses - $aliases{$1} = [ split(/\s+/, $2) ]; + $aliases{$1} = [ quotewords('\s+', 0, $2) ]; }}}, pine => sub { my $fh = shift; my $f='\t[^\t]*'; for (my $x = ''; defined($x); $x = $_) { @@ -537,7 +538,7 @@ if ($compose) { print C <<EOT; From $tpl_sender # This line is ignored. -GIT: Lines beginning in "GIT: " will be removed. +GIT: Lines beginning in "GIT:" will be removed. GIT: Consider including an overall diffstat or table of contents GIT: for the patch you are writing. GIT: @@ -552,8 +553,6 @@ EOT } close(C); - my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; - if ($annotate) { do_edit($compose_filename, @files); } else { @@ -570,7 +569,7 @@ EOT my $in_body = 0; my $summary_empty = 1; while(<C>) { - next if m/^GIT: /; + next if m/^GIT:/; if ($in_body) { $summary_empty = 0 unless (/^\n$/); } elsif (/^\n$/) { @@ -578,7 +577,7 @@ EOT if ($need_8bit_cte) { print C2 "MIME-Version: 1.0\n", "Content-Type: text/plain; ", - "charset=utf-8\n", + "charset=UTF-8\n", "Content-Transfer-Encoding: 8bit\n"; } } elsif (/^MIME-Version:/i) { @@ -767,12 +766,20 @@ sub unquote_rfc2047 { sub quote_rfc2047 { local $_ = shift; - my $encoding = shift || 'utf-8'; + my $encoding = shift || 'UTF-8'; s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg; s/(.*)/=\?$encoding\?q\?$1\?=/; return $_; } +sub is_rfc2047_quoted { + my $s = shift; + my $token = '[^][()<>@,;:"\/?.= \000-\037\177-\377]+'; + my $encoded_text = '[!->@-~]+'; + length($s) <= 75 && + $s =~ m/^(?:"[[:ascii:]]*"|=\?$token\?$token\?$encoded_text\?=)$/o; +} + # use the simplest quoting being able to handle the recipient sub sanitize_address { @@ -784,7 +791,7 @@ sub sanitize_address } # if recipient_name is already quoted, do nothing - if ($recipient_name =~ /^("[[:ascii:]]*"|=\?utf-8\?q\?.*\?=)$/) { + if (is_rfc2047_quoted($recipient_name)) { return $recipient; } @@ -804,6 +811,10 @@ sub sanitize_address } +# Returns 1 if the message was sent, and 0 otherwise. +# In actuality, the whole program dies when a there +# is an error sending a message. + sub send_message { my @recipients = unique_email_list(@to); @@ -872,7 +883,7 @@ X-Mailer: git-send-email $gitversion default => $ask_default); die "Send this email reply required" unless defined $_; if (/^n/i) { - return; + return 0; } elsif (/^q/i) { cleanup_compose_files(); exit(0); @@ -953,7 +964,7 @@ X-Mailer: git-send-email $gitversion $smtp->data or die $smtp->message; $smtp->datasend("$header\n$message") or die $smtp->message; $smtp->dataend() or die $smtp->message; - $smtp->ok or die "Failed to send $subject\n".$smtp->message; + $smtp->code =~ /250|200/ or die "Failed to send $subject\n".$smtp->message; } if ($quiet) { printf (($dry_run ? "Dry-" : "")."Sent %s\n", $subject); @@ -974,6 +985,8 @@ X-Mailer: git-send-email $gitversion print "Result: OK\n"; } } + + return 1; } $reply_to = $initial_reply_to; @@ -1134,10 +1147,10 @@ foreach my $t (@files) { @cc = (@initial_cc, @cc); - send_message(); + my $message_was_sent = send_message(); # set up for the next message - if ($chain_reply_to || !defined $reply_to || length($reply_to) == 0) { + if ($message_was_sent and $chain_reply_to || not defined $reply_to || length($reply_to) == 0) { $reply_to = $message_id; if (length $references > 0) { $references .= "\n $message_id"; diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 838233926f..80acb7de72 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -11,6 +11,34 @@ # exporting it. unset CDPATH +git_broken_path_fix () { + case ":$PATH:" in + *:$1:*) : ok ;; + *) + PATH=$( + SANE_TOOL_PATH="$1" + IFS=: path= sep= + set x $PATH + shift + for elem + do + case "$SANE_TOOL_PATH:$elem" in + (?*:/bin | ?*:/usr/bin) + path="$path$sep$SANE_TOOL_PATH" + sep=: + SANE_TOOL_PATH= + esac + path="$path$sep$elem" + sep=: + done + echo "$path" + ) + ;; + esac +} + +# @@BROKEN_PATH_FIX@@ + die() { echo >&2 "$@" exit 1 diff --git a/git-stash.sh b/git-stash.sh index b9ace99704..e6a5867209 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -3,8 +3,8 @@ dashless=$(basename "$0" | sed -e 's/-/ /') USAGE="list [<options>] - or: $dashless (show | drop | pop ) [<stash>] - or: $dashless apply [--index] [<stash>] + or: $dashless ( show | drop ) [<stash>] + or: $dashless ( pop | apply ) [--index] [<stash>] or: $dashless branch <branchname> [<stash>] or: $dashless [save [--keep-index] [<message>]] or: $dashless clear" diff --git a/git-submodule.sh b/git-submodule.sh index 8e234a4028..19a3a840fd 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -15,8 +15,10 @@ require_work_tree command= branch= quiet= +reference= cached= nofetch= +update= # # print stuff on stdout unless -q was specified @@ -91,6 +93,7 @@ module_clone() { path=$1 url=$2 + reference="$3" # If there already is a directory at the submodule path, # expect it to be empty (since that is the default checkout @@ -106,7 +109,12 @@ module_clone() test -e "$path" && die "A file already exist at path '$path'" - git-clone -n "$url" "$path" || + if test -n "$reference" + then + git-clone "$reference" -n "$url" "$path" + else + git-clone -n "$url" "$path" + fi || die "Clone of '$url' into submodule path '$path' failed" } @@ -131,6 +139,15 @@ cmd_add() -q|--quiet) quiet=1 ;; + --reference) + case "$2" in '') usage ;; esac + reference="--reference=$2" + shift + ;; + --reference=*) + reference="$1" + shift + ;; --) shift break @@ -203,7 +220,7 @@ cmd_add() git config submodule."$path".url "$url" else - module_clone "$path" "$realrepo" || exit + module_clone "$path" "$realrepo" "$reference" || exit ( unset GIT_DIR cd "$path" && @@ -294,6 +311,11 @@ cmd_init() git config submodule."$name".url "$url" || die "Failed to register url for submodule path '$path'" + upd="$(git config -f .gitmodules submodule."$name".update)" + test -z "$upd" || + git config submodule."$name".update "$upd" || + die "Failed to register update mode for submodule path '$path'" + say "Submodule '$name' ($url) registered for path '$path'" done } @@ -314,13 +336,26 @@ cmd_update() quiet=1 ;; -i|--init) + init=1 shift - cmd_init "$@" || return ;; -N|--no-fetch) shift nofetch=1 ;; + -r|--rebase) + shift + update="rebase" + ;; + --reference) + case "$2" in '') usage ;; esac + reference="--reference=$2" + shift 2 + ;; + --reference=*) + reference="$1" + shift + ;; --) shift break @@ -334,11 +369,17 @@ cmd_update() esac done + if test -n "$init" + then + cmd_init "--" "$@" || return + fi + module_list "$@" | while read mode sha1 stage path do name=$(module_name "$path") || exit url=$(git config submodule."$name".url) + update_module=$(git config submodule."$name".update) if test -z "$url" then # Only mention uninitialized submodules when its @@ -351,7 +392,7 @@ cmd_update() if ! test -d "$path"/.git -o -f "$path"/.git then - module_clone "$path" "$url" || exit + module_clone "$path" "$url" "$reference"|| exit subsha1= else subsha1=$(unset GIT_DIR; cd "$path" && @@ -359,6 +400,11 @@ cmd_update() die "Unable to find current revision in submodule path '$path'" fi + if ! test -z "$update" + then + update_module=$update + fi + if test "$subsha1" != "$sha1" then force= @@ -374,11 +420,22 @@ cmd_update() die "Unable to fetch in submodule path '$path'" fi - (unset GIT_DIR; cd "$path" && - git-checkout $force -q "$sha1") || - die "Unable to checkout '$sha1' in submodule path '$path'" + case "$update_module" in + rebase) + command="git rebase" + action="rebase" + msg="rebased onto" + ;; + *) + command="git checkout $force -q" + action="checkout" + msg="checked out" + ;; + esac - say "Submodule path '$path': checked out '$sha1'" + (unset GIT_DIR; cd "$path" && $command "$sha1") || + die "Unable to $action '$sha1' in submodule path '$path'" + say "Submodule path '$path': $msg '$sha1'" fi done } diff --git a/git-svn.perl b/git-svn.perl index ef1d30db38..33017974d0 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -5,7 +5,7 @@ use warnings; use strict; use vars qw/ $AUTHOR $VERSION $sha1 $sha1_short $_revision $_repository - $_q $_authors %users/; + $_q $_authors $_authors_prog %users/; $AUTHOR = 'Eric Wong <normalperson@yhbt.net>'; $VERSION = '@@GIT_VERSION@@'; @@ -39,6 +39,7 @@ use Digest::MD5; use IO::File qw//; use File::Basename qw/dirname basename/; use File::Path qw/mkpath/; +use File::Spec; use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/; use IPC::Open3; use Git; @@ -76,6 +77,7 @@ my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username, 'ignore-paths=s' => \$SVN::Git::Fetcher::_ignore_regex ); my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent, 'authors-file|A=s' => \$_authors, + 'authors-prog=s' => \$_authors_prog, 'repack:i' => \$Git::SVN::_repack, 'noMetadata' => \$Git::SVN::_no_metadata, 'useSvmProps' => \$Git::SVN::_use_svm_props, @@ -263,6 +265,9 @@ usage(0) if $_help; version() if $_version; usage(1) unless defined $cmd; load_authors() if $_authors; +if (defined $_authors_prog) { + $_authors_prog = "'" . File::Spec->rel2abs($_authors_prog) . "'"; +} unless ($cmd =~ /^(?:clone|init|multi-init|commit-diff)$/) { Git::SVN::Migration::migration_check(); @@ -361,6 +366,7 @@ sub cmd_clone { $path = basename($url) if !defined $path || !length $path; cmd_init($url, $path); Git::SVN::fetch_all($Git::SVN::default_repo_id); + command_oneline('config', 'svn.authorsfile', $_authors) if $_authors; } sub cmd_init { @@ -1172,16 +1178,27 @@ sub get_commit_entry { } rename $commit_editmsg, $commit_msg or croak $!; { + require Encode; # SVN requires messages to be UTF-8 when entering the repo local $/; open $log_fh, '<', $commit_msg or croak $!; binmode $log_fh; chomp($log_entry{log} = <$log_fh>); - if (my $enc = Git::config('i18n.commitencoding')) { - require Encode; - Encode::from_to($log_entry{log}, $enc, 'UTF-8'); + my $enc = Git::config('i18n.commitencoding') || 'UTF-8'; + my $msg = $log_entry{log}; + + eval { $msg = Encode::decode($enc, $msg, 1) }; + if ($@) { + die "Could not decode as $enc:\n", $msg, + "\nPerhaps you need to set i18n.commitencoding\n"; } + + eval { $msg = Encode::encode('UTF-8', $msg, 1) }; + die "Could not encode as UTF-8:\n$msg\n" if $@; + + $log_entry{log} = $msg; + close $log_fh or croak $!; } unlink $commit_msg; @@ -2663,12 +2680,33 @@ sub other_gs { $gs } +sub call_authors_prog { + my ($orig_author) = @_; + my $author = `$::_authors_prog $orig_author`; + if ($? != 0) { + die "$::_authors_prog failed with exit code $?\n" + } + if ($author =~ /^\s*(.+?)\s*<(.*)>\s*$/) { + my ($name, $email) = ($1, $2); + $email = undef if length $2 == 0; + return [$name, $email]; + } else { + die "Author: $orig_author: $::_authors_prog returned " + . "invalid author format: $author\n"; + } +} + sub check_author { my ($author) = @_; if (!defined $author || length $author == 0) { $author = '(no author)'; - } elsif (defined $::_authors && ! defined $::users{$author}) { - die "Author: $author not defined in $::_authors file\n"; + } + if (!defined $::users{$author}) { + if (defined $::_authors_prog) { + $::users{$author} = call_authors_prog($author); + } elsif (defined $::_authors) { + die "Author: $author not defined in $::_authors file\n"; + } } $author; } @@ -4438,6 +4476,7 @@ sub gs_fetch_loop_common { my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc); my $longest_path = longest_common_path($gsv, $globs); my $ra_url = $self->{url}; + my $find_trailing_edge; while (1) { my %revs; my $err; @@ -4455,8 +4494,10 @@ sub gs_fetch_loop_common { sub { $revs{$_[1]} = _cb(@_) }); if ($err) { print "Checked through r$max\r"; + } else { + $find_trailing_edge = 1; } - if ($err && $max >= $head) { + if ($err and $find_trailing_edge) { print STDERR "Path '$longest_path' ", "was probably deleted:\n", $err->expanded_message, @@ -4468,13 +4509,14 @@ sub gs_fetch_loop_common { my $ok; $self->get_log([$longest_path], $min, $hi, 0, 1, 1, sub { - $ok ||= $_[1]; + $ok = $_[1]; $revs{$_[1]} = _cb(@_) }); if ($ok) { print STDERR "r$min .. r$ok OK\n"; last; } } + $find_trailing_edge = 0; } $SVN::Error::handler = $err_handler; @@ -327,6 +327,7 @@ static void handle_internal_command(int argc, const char **argv) { "merge-ours", cmd_merge_ours, RUN_SETUP }, { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE }, { "merge-subtree", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE }, + { "mktree", cmd_mktree, RUN_SETUP }, { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE }, { "name-rev", cmd_name_rev, RUN_SETUP }, { "pack-objects", cmd_pack_objects, RUN_SETUP }, diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 3f99361ed0..1e7e2d8387 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -458,8 +458,8 @@ sub filter_snapshot_fmts { @fmts = map { exists $known_snapshot_format_aliases{$_} ? $known_snapshot_format_aliases{$_} : $_} @fmts; - @fmts = grep(exists $known_snapshot_formats{$_}, @fmts); - + @fmts = grep { + exists $known_snapshot_formats{$_} } @fmts; } our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++"; @@ -690,9 +690,10 @@ sub evaluate_path_info { # format key itself, with a prepended dot while (my ($fmt, $opt) = each %known_snapshot_formats) { my $hash = $refname; - my $sfx; - $hash =~ s/(\Q$opt->{'suffix'}\E|\Q.$fmt\E)$//; - next unless $sfx = $1; + unless ($hash =~ s/(\Q$opt->{'suffix'}\E|\Q.$fmt\E)$//) { + next; + } + my $sfx = $1; # a valid suffix was found, so set the snapshot format # and reset the hash parameter $input_params{'snapshot_format'} = $fmt; @@ -828,7 +829,7 @@ if (!defined $action) { if (!defined($actions{$action})) { die_error(400, "Unknown action"); } -if ($action !~ m/^(opml|project_list|project_index)$/ && +if ($action !~ m/^(?:opml|project_list|project_index)$/ && !$project) { die_error(400, "Project needed"); } @@ -838,7 +839,7 @@ exit; ## ====================================================================== ## action links -sub href (%) { +sub href { my %params = @_; # default is to use -absolute url() i.e. $my_uri my $href = $params{-full} ? $my_url : $my_uri; @@ -1036,7 +1037,7 @@ sub esc_url { } # replace invalid utf8 character with SUBSTITUTION sequence -sub esc_html ($;%) { +sub esc_html { my $str = shift; my %opts = @_; @@ -1235,7 +1236,7 @@ sub chop_and_escape_str { if ($chopped eq $str) { return esc_html($chopped); } else { - $str =~ s/([[:cntrl:]])/?/g; + $str =~ s/[[:cntrl:]]/?/g; return $cgi->span({-title=>$str}, esc_html($chopped)); } } @@ -1296,7 +1297,7 @@ use constant { }; # submodule/subproject, a commit object reference -sub S_ISGITLINK($) { +sub S_ISGITLINK { my $mode = shift; return (($mode & S_IFMT) == S_IFGITLINK) @@ -1458,6 +1459,7 @@ sub format_subject_html { $extra = '' unless defined($extra); if (length($short) < length($long)) { + $long =~ s/[[:cntrl:]]/?/g; return $cgi->a({-href => $href, -class => "list subject", -title => to_utf8($long)}, esc_html($short) . $extra); @@ -1838,7 +1840,7 @@ sub git_cmd { # Try to avoid using this function wherever possible. sub quote_command { return join(' ', - map( { my $a = $_; $a =~ s/(['!])/'\\$1'/g; "'$a'" } @_ )); + map { my $a = $_; $a =~ s/(['!])/'\\$1'/g; "'$a'" } @_ ); } # get HEAD ref of given project as hash @@ -2050,7 +2052,7 @@ sub git_get_project_description { my $path = shift; $git_dir = "$projectroot/$path"; - open my $fd, "$git_dir/description" + open my $fd, '<', "$git_dir/description" or return git_get_project_config('description'); my $descr = <$fd>; close $fd; @@ -2065,18 +2067,17 @@ sub git_get_project_ctags { my $ctags = {}; $git_dir = "$projectroot/$path"; - unless (opendir D, "$git_dir/ctags") { - return $ctags; - } - foreach (grep { -f $_ } map { "$git_dir/ctags/$_" } readdir(D)) { - open CT, $_ or next; - my $val = <CT>; + opendir my $dh, "$git_dir/ctags" + or return $ctags; + foreach (grep { -f $_ } map { "$git_dir/ctags/$_" } readdir($dh)) { + open my $ct, '<', $_ or next; + my $val = <$ct>; chomp $val; - close CT; + close $ct; my $ctag = $_; $ctag =~ s#.*/##; $ctags->{$ctag} = $val; } - closedir D; + closedir $dh; $ctags; } @@ -2129,7 +2130,7 @@ sub git_get_project_url_list { my $path = shift; $git_dir = "$projectroot/$path"; - open my $fd, "$git_dir/cloneurl" + open my $fd, '<', "$git_dir/cloneurl" or return wantarray ? @{ config_to_multi(git_get_project_config('url')) } : config_to_multi(git_get_project_config('url')); @@ -2187,7 +2188,7 @@ sub git_get_projects_list { # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin' # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman' my %paths; - open my ($fd), $projects_list or return; + open my $fd, '<', $projects_list or return; PROJECT: while (my $line = <$fd>) { chomp $line; @@ -2250,7 +2251,7 @@ sub git_get_project_list_from_file { # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin' # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman' if (-f $projects_list) { - open (my $fd , $projects_list); + open(my $fd, '<', $projects_list); while (my $line = <$fd>) { chomp $line; my ($pr, $ow) = split ' ', $line; @@ -2615,7 +2616,7 @@ sub parsed_difftree_line { } # parse line of git-ls-tree output -sub parse_ls_tree_line ($;%) { +sub parse_ls_tree_line { my $line = shift; my %opts = @_; my %res; @@ -2804,18 +2805,18 @@ sub mimetype_guess_file { -r $mimemap or return undef; my %mimemap; - open(MIME, $mimemap) or return undef; - while (<MIME>) { + open(my $mh, '<', $mimemap) or return undef; + while (<$mh>) { next if m/^#/; # skip comments - my ($mime, $exts) = split(/\t+/); + my ($mimetype, $exts) = split(/\t+/); if (defined $exts) { my @exts = split(/\s+/, $exts); foreach my $ext (@exts) { - $mimemap{$ext} = $mime; + $mimemap{$ext} = $mimetype; } } } - close(MIME); + close($mh); $filename =~ /\.([^.]*)$/; return $mimemap{$1}; @@ -3213,7 +3214,6 @@ sub git_print_header_div { "\n</div>\n"; } -#sub git_print_authorship (\%) { sub git_print_authorship { my $co = shift; @@ -3269,8 +3269,7 @@ sub git_print_page_path { print "<br/></div>\n"; } -# sub git_print_log (\@;%) { -sub git_print_log ($;%) { +sub git_print_log { my $log = shift; my %opts = @_; @@ -3328,7 +3327,7 @@ sub git_get_link_target { open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash or return; { - local $/; + local $/ = undef; $link_target = <$fd>; } close $fd @@ -3341,10 +3340,7 @@ sub git_get_link_target { # return target of link relative to top directory (top tree); # return undef if it is not possible (including absolute links). sub normalize_link_target { - my ($link_target, $basedir, $hash_base) = @_; - - # we can normalize symlink target only if $hash_base is provided - return unless $hash_base; + my ($link_target, $basedir) = @_; # absolute symlinks (beginning with '/') cannot be normalized return if (substr($link_target, 0, 1) eq '/'); @@ -3400,7 +3396,7 @@ sub git_print_tree_entry { if (S_ISLNK(oct $t->{'mode'})) { my $link_target = git_get_link_target($t->{'hash'}); if ($link_target) { - my $norm_target = normalize_link_target($link_target, $basedir, $hash_base); + my $norm_target = normalize_link_target($link_target, $basedir); if (defined $norm_target) { print " -> " . $cgi->a({-href => href(action=>"object", hash_base=>$hash_base, @@ -3993,7 +3989,7 @@ sub fill_project_list_info { ($pname !~ /\/$/) && (-d "$projectroot/$pname")) { $pr->{'forks'} = "-d $projectroot/$pname"; - } else { + } else { $pr->{'forks'} = 0; } } @@ -4803,11 +4799,10 @@ sub git_blob_plain { -content_disposition => ($sandbox ? 'attachment' : 'inline') . '; filename="' . $save_as . '"'); - undef $/; + local $/ = undef; binmode STDOUT, ':raw'; print <$fd>; binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi - $/ = "\n"; close $fd; } @@ -4909,12 +4904,16 @@ sub git_tree { } } die_error(404, "No such tree") unless defined($hash); - $/ = "\0"; - open my $fd, "-|", git_cmd(), "ls-tree", '-z', $hash - or die_error(500, "Open git-ls-tree failed"); - my @entries = map { chomp; $_ } <$fd>; - close $fd or die_error(404, "Reading tree failed"); - $/ = "\n"; + + my @entries = (); + { + local $/ = "\0"; + open my $fd, "-|", git_cmd(), "ls-tree", '-z', $hash + or die_error(500, "Open git-ls-tree failed"); + @entries = map { chomp; $_ } <$fd>; + close $fd + or die_error(404, "Reading tree failed"); + } my $refs = git_get_references(); my $ref = format_ref_marker($refs, $hash_base); @@ -5809,7 +5808,7 @@ sub git_search { print "<table class=\"pickaxe search\">\n"; my $alternate = 1; - $/ = "\n"; + local $/ = "\n"; open my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts, '--pretty=format:%H', '--no-abbrev', '--raw', "-S$searchtext", ($search_use_regexp ? '--pickaxe-regex' : ()); @@ -5879,7 +5878,7 @@ sub git_search { print "<table class=\"grep_search\">\n"; my $alternate = 1; my $matches = 0; - $/ = "\n"; + local $/ = "\n"; open my $fd, "-|", git_cmd(), 'grep', '-n', $search_use_regexp ? ('-E', '-i') : '-F', $searchtext, $co{'tree'}; @@ -6282,7 +6281,7 @@ XML # end of feed if ($format eq 'rss') { print "</channel>\n</rss>\n"; - } elsif ($format eq 'atom') { + } elsif ($format eq 'atom') { print "</feed>\n"; } } @@ -47,20 +47,6 @@ static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb); * - Limit the number of columns, similar to the way gitk does. * If we reach more than a specified number of columns, omit * sections of some columns. - * - * - The output during the GRAPH_PRE_COMMIT and GRAPH_COLLAPSING states - * could be made more compact by printing horizontal lines, instead of - * long diagonal lines. For example, during collapsing, something like - * this: instead of this: - * | | | | | | | | | | - * | |_|_|/ | | | |/ - * |/| | | | | |/| - * | | | | | |/| | - * |/| | | - * | | | | - * - * If there are several parallel diagonal lines, they will need to be - * replaced with horizontal lines on subsequent rows. */ struct column { @@ -982,6 +968,9 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf { int i; int *tmp_mapping; + short used_horizontal = 0; + int horizontal_edge = -1; + int horizontal_edge_target = -1; /* * Clear out the new_mapping array @@ -1019,6 +1008,23 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf * Move to the left by one */ graph->new_mapping[i - 1] = target; + /* + * If there isn't already an edge moving horizontally + * select this one. + */ + if (horizontal_edge == -1) { + int j; + horizontal_edge = i; + horizontal_edge_target = target; + /* + * The variable target is the index of the graph + * column, and therefore target*2+3 is the + * actual screen column of the first horizontal + * line. + */ + for (j = (target * 2)+3; j < (i - 2); j += 2) + graph->new_mapping[j] = target; + } } else if (graph->new_mapping[i - 1] == target) { /* * There is a branch line to our left @@ -1039,10 +1045,21 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf * * The space just to the left of this * branch should always be empty. + * + * The branch to the left of that space + * should be our eventual target. */ assert(graph->new_mapping[i - 1] > target); assert(graph->new_mapping[i - 2] < 0); + assert(graph->new_mapping[i - 3] == target); graph->new_mapping[i - 2] = target; + /* + * Mark this branch as the horizontal edge to + * prevent any other edges from moving + * horizontally. + */ + if (horizontal_edge == -1) + horizontal_edge = i; } } @@ -1061,8 +1078,23 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf strbuf_addch(sb, ' '); else if (target * 2 == i) strbuf_write_column(sb, &graph->new_columns[target], '|'); - else + else if (target == horizontal_edge_target && + i != horizontal_edge - 1) { + /* + * Set the mappings for all but the + * first segment to -1 so that they + * won't continue into the next line. + */ + if (i != (target * 2)+3) + graph->new_mapping[i] = -1; + used_horizontal = 1; + strbuf_write_column(sb, &graph->new_columns[target], '_'); + } else { + if (used_horizontal && i < horizontal_edge) + graph->new_mapping[i] = -1; strbuf_write_column(sb, &graph->new_columns[target], '/'); + + } } graph_pad_horizontally(graph, sb, graph->mapping_size); @@ -331,7 +331,7 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, if (hit && p->word_regexp) { if ((pmatch[0].rm_so < 0) || - (eol - bol) <= pmatch[0].rm_so || + (eol - bol) < pmatch[0].rm_so || (pmatch[0].rm_eo < 0) || (eol - bol) < pmatch[0].rm_eo) die("regexp returned nonsense"); @@ -350,6 +350,10 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, else hit = 0; + /* Words consist of at least one character. */ + if (pmatch->rm_so == pmatch->rm_eo) + hit = 0; + if (!hit && pmatch[0].rm_so + bol + 1 < eol) { /* There could be more than one match on the * line, and the first match might not be @@ -360,6 +364,7 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, bol = pmatch[0].rm_so + bol + 1; while (word_char(bol[-1]) && bol < eol) bol++; + eflags |= REG_NOTBOL; if (bol < eol) goto again; } @@ -499,6 +504,8 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol, *eol = '\0'; while (next_match(opt, bol, eol, ctx, &match, eflags)) { + if (match.rm_so == match.rm_eo) + break; printf("%.*s%s%.*s%s", (int)match.rm_so, bol, opt->color_match, @@ -61,23 +61,23 @@ struct grep_opt { struct grep_expr *pattern_expression; int prefix_length; regex_t regexp; - unsigned linenum:1; - unsigned invert:1; - unsigned status_only:1; - unsigned name_only:1; - unsigned unmatch_name_only:1; - unsigned count:1; - unsigned word_regexp:1; - unsigned fixed:1; - unsigned all_match:1; + int linenum; + int invert; + int status_only; + int name_only; + int unmatch_name_only; + int count; + int word_regexp; + int fixed; + int all_match; #define GREP_BINARY_DEFAULT 0 #define GREP_BINARY_NOMATCH 1 #define GREP_BINARY_TEXT 2 - unsigned binary:2; - unsigned extended:1; - unsigned relative:1; - unsigned pathname:1; - unsigned null_following_name:1; + int binary; + int extended; + int relative; + int pathname; + int null_following_name; int color; char color_match[COLOR_MAXLEN]; const char *color_external; diff --git a/hash-object.c b/hash-object.c index ebb3bedb07..47cf43c3cd 100644 --- a/hash-object.c +++ b/hash-object.c @@ -84,7 +84,8 @@ int main(int argc, const char **argv) git_extract_argv0_path(argv[0]); - argc = parse_options(argc, argv, hash_object_options, hash_object_usage, 0); + argc = parse_options(argc, argv, NULL, hash_object_options, + hash_object_usage, 0); if (write_object) { prefix = setup_git_directory(); diff --git a/http-push.c b/http-push.c index e16a0ad3f9..a7e28e345f 100644 --- a/http-push.c +++ b/http-push.c @@ -1844,7 +1844,7 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock) return 1; } -static struct ref *remote_refs, **remote_tail; +static struct ref *remote_refs; static void one_remote_ref(char *refname) { @@ -1874,27 +1874,15 @@ static void one_remote_ref(char *refname) } } - *remote_tail = ref; - remote_tail = &ref->next; + ref->next = remote_refs; + remote_refs = ref; } static void get_dav_remote_heads(void) { - remote_tail = &remote_refs; remote_ls("refs/", (PROCESS_FILES | PROCESS_DIRS | RECURSIVE), process_ls_ref, NULL); } -static int is_zero_sha1(const unsigned char *sha1) -{ - int i; - - for (i = 0; i < 20; i++) { - if (*sha1++) - return 0; - } - return 1; -} - static void add_remote_info_ref(struct remote_ls_ctx *ls) { struct strbuf *buf = (struct strbuf *)ls->userData; @@ -2120,13 +2108,13 @@ static int delete_remote_branch(char *pattern, int force) /* Remote HEAD must resolve to a known object */ if (symref) return error("Remote HEAD symrefs too deep"); - if (is_zero_sha1(head_sha1)) + if (is_null_sha1(head_sha1)) return error("Unable to resolve remote HEAD"); if (!has_sha1_file(head_sha1)) return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", sha1_to_hex(head_sha1)); /* Remote branch must resolve to a known object */ - if (is_zero_sha1(remote_ref->old_sha1)) + if (is_null_sha1(remote_ref->old_sha1)) return error("Unable to resolve remote branch %s", remote_ref->name); if (!has_sha1_file(remote_ref->old_sha1)) @@ -2311,9 +2299,7 @@ int main(int argc, char **argv) } /* match them up */ - if (!remote_tail) - remote_tail = &remote_refs; - if (match_refs(local_refs, remote_refs, &remote_tail, + if (match_refs(local_refs, &remote_refs, nr_refspec, (const char **) refspec, push_all)) { rc = -1; goto cleanup; @@ -2327,14 +2313,14 @@ int main(int argc, char **argv) new_refs = 0; for (ref = remote_refs; ref; ref = ref->next) { char old_hex[60], *new_hex; - const char *commit_argv[4]; + const char *commit_argv[5]; int commit_argc; char *new_sha1_hex, *old_sha1_hex; if (!ref->peer_ref) continue; - if (is_zero_sha1(ref->peer_ref->new_sha1)) { + if (is_null_sha1(ref->peer_ref->new_sha1)) { if (delete_remote_branch(ref->name, 1) == -1) { error("Could not remove %s", ref->name); rc = -4; @@ -2350,7 +2336,7 @@ int main(int argc, char **argv) } if (!force_all && - !is_zero_sha1(ref->old_sha1) && + !is_null_sha1(ref->old_sha1) && !ref->force) { if (!has_sha1_file(ref->old_sha1) || !ref_newer(ref->peer_ref->new_sha1, @@ -2400,13 +2386,14 @@ int main(int argc, char **argv) old_sha1_hex = NULL; commit_argv[1] = "--objects"; commit_argv[2] = new_sha1_hex; - if (!push_all && !is_zero_sha1(ref->old_sha1)) { + if (!push_all && !is_null_sha1(ref->old_sha1)) { old_sha1_hex = xmalloc(42); sprintf(old_sha1_hex, "^%s", sha1_to_hex(ref->old_sha1)); commit_argv[3] = old_sha1_hex; commit_argc++; } + commit_argv[commit_argc] = NULL; init_revisions(&revs, setup_git_directory()); setup_revisions(commit_argc, commit_argv, &revs, NULL); revs.edge_hint = 0; /* just in case */ diff --git a/imap-send.c b/imap-send.c index 8154cb2116..e4c83b9d5b 100644 --- a/imap-send.c +++ b/imap-send.c @@ -982,9 +982,7 @@ static struct store *imap_open_store(struct imap_server_conf *srvc) struct imap_store *ctx; struct imap *imap; char *arg, *rsp; - struct hostent *he; - struct sockaddr_in addr; - int s, a[2], preauth; + int s = -1, a[2], preauth; pid_t pid; ctx = xcalloc(sizeof(*ctx), 1); @@ -1021,6 +1019,51 @@ static struct store *imap_open_store(struct imap_server_conf *srvc) imap_info("ok\n"); } else { +#ifndef NO_IPV6 + struct addrinfo hints, *ai0, *ai; + int gai; + char portstr[6]; + + snprintf(portstr, sizeof(portstr), "%hu", srvc->port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + imap_info("Resolving %s... ", srvc->host); + gai = getaddrinfo(srvc->host, portstr, &hints, &ai); + if (gai) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai)); + goto bail; + } + imap_info("ok\n"); + + for (ai0 = ai; ai; ai = ai->ai_next) { + char addr[NI_MAXHOST]; + + s = socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol); + if (s < 0) + continue; + + getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, + sizeof(addr), NULL, 0, NI_NUMERICHOST); + imap_info("Connecting to [%s]:%s... ", addr, portstr); + + if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) { + close(s); + s = -1; + perror("connect"); + continue; + } + + break; + } + freeaddrinfo(ai0); +#else /* NO_IPV6 */ + struct hostent *he; + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); addr.sin_port = htons(srvc->port); addr.sin_family = AF_INET; @@ -1040,7 +1083,12 @@ static struct store *imap_open_store(struct imap_server_conf *srvc) imap_info("Connecting to %s:%hu... ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) { close(s); + s = -1; perror("connect"); + } +#endif + if (s < 0) { + fputs("Error: unable to connect to server.\n", stderr); goto bail; } diff --git a/ll-merge.c b/ll-merge.c index 81c02ad053..31d6f0a2ee 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -175,8 +175,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn, { "B", temp[2] }, { NULL } }; - struct child_process child; - const char *args[20]; + const char *args[] = { "sh", "-c", NULL, NULL }; int status, fd, i; struct stat st; @@ -191,14 +190,8 @@ static int ll_ext_merge(const struct ll_merge_driver *fn, strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict); - memset(&child, 0, sizeof(child)); - child.argv = args; - args[0] = "sh"; - args[1] = "-c"; args[2] = cmd.buf; - args[3] = NULL; - - status = run_command(&child); + status = run_command_v_opt(args, 0); if (status < -ERR_RUN_COMMAND_FORK) ; /* failure in run-command */ else diff --git a/log-tree.c b/log-tree.c index 5bd29e6994..59d63eb67e 100644 --- a/log-tree.c +++ b/log-tree.c @@ -25,6 +25,7 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in struct object *obj = parse_object(sha1); if (!obj) return 0; + refname = prettify_refname(refname); add_name_decoration("", refname, obj); while (obj->type == OBJ_TAG) { obj = ((struct tag *)obj)->tagged; diff --git a/merge-index.c b/merge-index.c index aa9cf23a39..19ddd03e0b 100644 --- a/merge-index.c +++ b/merge-index.c @@ -3,45 +3,20 @@ #include "exec_cmd.h" static const char *pgm; -static const char *arguments[9]; static int one_shot, quiet; static int err; -static void run_program(void) -{ - struct child_process child; - memset(&child, 0, sizeof(child)); - child.argv = arguments; - if (run_command(&child)) { - if (one_shot) { - err++; - } else { - if (!quiet) - die("merge program failed"); - exit(1); - } - } -} - static int merge_entry(int pos, const char *path) { int found; + const char *arguments[] = { pgm, "", "", "", path, "", "", "", NULL }; + char hexbuf[4][60]; + char ownbuf[4][60]; if (pos >= active_nr) die("git merge-index: %s not in the cache", path); - arguments[0] = pgm; - arguments[1] = ""; - arguments[2] = ""; - arguments[3] = ""; - arguments[4] = path; - arguments[5] = ""; - arguments[6] = ""; - arguments[7] = ""; - arguments[8] = NULL; found = 0; do { - static char hexbuf[4][60]; - static char ownbuf[4][60]; struct cache_entry *ce = active_cache[pos]; int stage = ce_stage(ce); @@ -55,7 +30,16 @@ static int merge_entry(int pos, const char *path) } while (++pos < active_nr); if (!found) die("git merge-index: %s not in the cache", path); - run_program(); + + if (run_command_v_opt(arguments, 0)) { + if (one_shot) + err++; + else { + if (!quiet) + die("merge program failed"); + exit(1); + } + } return found; } diff --git a/mktree.c b/mktree.c deleted file mode 100644 index 137a0950f6..0000000000 --- a/mktree.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * GIT - the stupid content tracker - * - * Copyright (c) Junio C Hamano, 2006 - */ -#include "cache.h" -#include "quote.h" -#include "tree.h" -#include "exec_cmd.h" - -static struct treeent { - unsigned mode; - unsigned char sha1[20]; - int len; - char name[FLEX_ARRAY]; -} **entries; -static int alloc, used; - -static void append_to_tree(unsigned mode, unsigned char *sha1, char *path) -{ - struct treeent *ent; - int len = strlen(path); - if (strchr(path, '/')) - die("path %s contains slash", path); - - if (alloc <= used) { - alloc = alloc_nr(used); - entries = xrealloc(entries, sizeof(*entries) * alloc); - } - ent = entries[used++] = xmalloc(sizeof(**entries) + len + 1); - ent->mode = mode; - ent->len = len; - hashcpy(ent->sha1, sha1); - memcpy(ent->name, path, len+1); -} - -static int ent_compare(const void *a_, const void *b_) -{ - struct treeent *a = *(struct treeent **)a_; - struct treeent *b = *(struct treeent **)b_; - return base_name_compare(a->name, a->len, a->mode, - b->name, b->len, b->mode); -} - -static void write_tree(unsigned char *sha1) -{ - struct strbuf buf; - size_t size; - int i; - - qsort(entries, used, sizeof(*entries), ent_compare); - for (size = i = 0; i < used; i++) - size += 32 + entries[i]->len; - - strbuf_init(&buf, size); - for (i = 0; i < used; i++) { - struct treeent *ent = entries[i]; - strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0'); - strbuf_add(&buf, ent->sha1, 20); - } - - write_sha1_file(buf.buf, buf.len, tree_type, sha1); -} - -static const char mktree_usage[] = "git mktree [-z]"; - -int main(int ac, char **av) -{ - struct strbuf sb = STRBUF_INIT; - struct strbuf p_uq = STRBUF_INIT; - unsigned char sha1[20]; - int line_termination = '\n'; - - git_extract_argv0_path(av[0]); - - setup_git_directory(); - - while ((1 < ac) && av[1][0] == '-') { - char *arg = av[1]; - if (!strcmp("-z", arg)) - line_termination = 0; - else - usage(mktree_usage); - ac--; - av++; - } - - while (strbuf_getline(&sb, stdin, line_termination) != EOF) { - char *ptr, *ntr; - unsigned mode; - enum object_type type; - char *path; - - ptr = sb.buf; - /* Input is non-recursive ls-tree output format - * mode SP type SP sha1 TAB name - */ - mode = strtoul(ptr, &ntr, 8); - if (ptr == ntr || !ntr || *ntr != ' ') - die("input format error: %s", sb.buf); - ptr = ntr + 1; /* type */ - ntr = strchr(ptr, ' '); - if (!ntr || sb.buf + sb.len <= ntr + 40 || - ntr[41] != '\t' || - get_sha1_hex(ntr + 1, sha1)) - die("input format error: %s", sb.buf); - type = sha1_object_info(sha1, NULL); - if (type < 0) - die("object %s unavailable", sha1_to_hex(sha1)); - *ntr++ = 0; /* now at the beginning of SHA1 */ - if (type != type_from_string(ptr)) - die("object type %s mismatch (%s)", ptr, typename(type)); - - path = ntr + 41; /* at the beginning of name */ - if (line_termination && path[0] == '"') { - strbuf_reset(&p_uq); - if (unquote_c_style(&p_uq, path, NULL)) { - die("invalid quoting"); - } - path = p_uq.buf; - } - - append_to_tree(mode, sha1, path); - } - strbuf_release(&p_uq); - strbuf_release(&sb); - - write_tree(sha1); - puts(sha1_to_hex(sha1)); - exit(0); -} @@ -52,7 +52,7 @@ static unsigned int hash_obj(struct object *obj, unsigned int n) static void insert_obj_hash(struct object *obj, struct object **hash, unsigned int size) { - int j = hash_obj(obj, size); + unsigned int j = hash_obj(obj, size); while (hash[j]) { j++; @@ -62,16 +62,16 @@ static void insert_obj_hash(struct object *obj, struct object **hash, unsigned i hash[j] = obj; } -static int hashtable_index(const unsigned char *sha1) +static unsigned int hashtable_index(const unsigned char *sha1) { unsigned int i; memcpy(&i, sha1, sizeof(unsigned int)); - return (int)(i % obj_hash_size); + return i % obj_hash_size; } struct object *lookup_object(const unsigned char *sha1) { - int i; + unsigned int i; struct object *obj; if (!obj_hash) diff --git a/parse-options.c b/parse-options.c index cf71bcffd2..79de18b37e 100644 --- a/parse-options.c +++ b/parse-options.c @@ -31,11 +31,20 @@ static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt, return 0; } +static void fix_filename(const char *prefix, const char **file) +{ + if (!file || !*file || !prefix || is_absolute_path(*file) + || !strcmp("-", *file)) + return; + *file = xstrdup(prefix_filename(prefix, strlen(prefix), *file)); +} + static int get_value(struct parse_opt_ctx_t *p, const struct option *opt, int flags) { const char *s, *arg; const int unset = flags & OPT_UNSET; + int err; if (unset && p->opt) return opterror(opt, "takes no value", flags); @@ -50,6 +59,7 @@ static int get_value(struct parse_opt_ctx_t *p, /* FALLTHROUGH */ case OPTION_BOOLEAN: case OPTION_BIT: + case OPTION_NEGBIT: case OPTION_SET_INT: case OPTION_SET_PTR: return opterror(opt, "takes no value", flags); @@ -66,6 +76,13 @@ static int get_value(struct parse_opt_ctx_t *p, *(int *)opt->value |= opt->defval; return 0; + case OPTION_NEGBIT: + if (unset) + *(int *)opt->value |= opt->defval; + else + *(int *)opt->value &= ~opt->defval; + return 0; + case OPTION_BOOLEAN: *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; return 0; @@ -87,6 +104,19 @@ static int get_value(struct parse_opt_ctx_t *p, return get_arg(p, opt, flags, (const char **)opt->value); return 0; + case OPTION_FILENAME: + err = 0; + if (unset) + *(const char **)opt->value = NULL; + else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) + *(const char **)opt->value = (const char *)opt->defval; + else + err = get_arg(p, opt, flags, (const char **)opt->value); + + if (!err) + fix_filename(p->prefix, (const char **)opt->value); + return err; + case OPTION_CALLBACK: if (unset) return (*opt->callback)(opt, NULL, 1) ? (-1) : 0; @@ -121,11 +151,33 @@ static int get_value(struct parse_opt_ctx_t *p, static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options) { + const struct option *numopt = NULL; + for (; options->type != OPTION_END; options++) { if (options->short_name == *p->opt) { p->opt = p->opt[1] ? p->opt + 1 : NULL; return get_value(p, options, OPT_SHORT); } + + /* + * Handle the numerical option later, explicit one-digit + * options take precedence over it. + */ + if (options->type == OPTION_NUMBER) + numopt = options; + } + if (numopt && isdigit(*p->opt)) { + size_t len = 1; + char *arg; + int rc; + + while (isdigit(p->opt[len])) + len++; + arg = xmemdupz(p->opt, len); + p->opt = p->opt[len] ? p->opt + len : NULL; + rc = (*numopt->callback)(numopt, arg, 0) ? (-1) : 0; + free(arg); + return rc; } return -2; } @@ -215,6 +267,25 @@ is_abbreviated: return -2; } +static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg, + const struct option *options) +{ + for (; options->type != OPTION_END; options++) { + if (!(options->flags & PARSE_OPT_NODASH)) + continue; + if ((options->flags & PARSE_OPT_OPTARG) || + !(options->flags & PARSE_OPT_NOARG)) + die("BUG: dashless options don't support arguments"); + if (!(options->flags & PARSE_OPT_NONEG)) + die("BUG: dashless options don't support negation"); + if (options->long_name) + die("BUG: dashless options can't be long"); + if (options->short_name == arg[0] && arg[1] == '\0') + return get_value(p, options, OPT_SHORT); + } + return -2; +} + static void check_typos(const char *arg, const struct option *options) { if (strlen(arg) < 3) @@ -235,13 +306,37 @@ static void check_typos(const char *arg, const struct option *options) } } +static void parse_options_check(const struct option *opts) +{ + int err = 0; + + for (; opts->type != OPTION_END; opts++) { + if ((opts->flags & PARSE_OPT_LASTARG_DEFAULT) && + (opts->flags & PARSE_OPT_OPTARG)) { + if (opts->long_name) { + error("`--%s` uses incompatible flags " + "LASTARG_DEFAULT and OPTARG", opts->long_name); + } else { + error("`-%c` uses incompatible flags " + "LASTARG_DEFAULT and OPTARG", opts->short_name); + } + err |= 1; + } + } + + if (err) + exit(129); +} + void parse_options_start(struct parse_opt_ctx_t *ctx, - int argc, const char **argv, int flags) + int argc, const char **argv, const char *prefix, + int flags) { memset(ctx, 0, sizeof(*ctx)); ctx->argc = argc - 1; ctx->argv = argv + 1; ctx->out = argv; + ctx->prefix = prefix; ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0); ctx->flags = flags; if ((flags & PARSE_OPT_KEEP_UNKNOWN) && @@ -258,6 +353,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, { int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); + parse_options_check(options); + /* we must reset ->opt, unknown short option leave it dangling */ ctx->opt = NULL; @@ -265,6 +362,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, const char *arg = ctx->argv[0]; if (*arg != '-' || !arg[1]) { + if (parse_nodash_opt(ctx, arg, options) == 0) + continue; if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) break; ctx->out[ctx->cpidx++] = ctx->argv[0]; @@ -338,12 +437,13 @@ int parse_options_end(struct parse_opt_ctx_t *ctx) return ctx->cpidx + ctx->argc; } -int parse_options(int argc, const char **argv, const struct option *options, - const char * const usagestr[], int flags) +int parse_options(int argc, const char **argv, const char *prefix, + const struct option *options, const char * const usagestr[], + int flags) { struct parse_opt_ctx_t ctx; - parse_options_start(&ctx, argc, argv, flags); + parse_options_start(&ctx, argc, argv, prefix, flags); switch (parse_options_step(&ctx, options, usagestr)) { case PARSE_OPT_HELP: exit(129); @@ -361,6 +461,20 @@ int parse_options(int argc, const char **argv, const struct option *options, return parse_options_end(&ctx); } +static int usage_argh(const struct option *opts) +{ + const char *s; + int literal = opts->flags & PARSE_OPT_LITERAL_ARGHELP; + if (opts->flags & PARSE_OPT_OPTARG) + if (opts->long_name) + s = literal ? "[=%s]" : "[=<%s>]"; + else + s = literal ? "[%s]" : "[<%s>]"; + else + s = literal ? " %s" : " <%s>"; + return fprintf(stderr, s, opts->argh); +} + #define USAGE_OPTS_WIDTH 24 #define USAGE_GAP 2 @@ -397,12 +511,18 @@ int usage_with_options_internal(const char * const *usagestr, continue; pos = fprintf(stderr, " "); - if (opts->short_name) - pos += fprintf(stderr, "-%c", opts->short_name); + if (opts->short_name) { + if (opts->flags & PARSE_OPT_NODASH) + pos += fprintf(stderr, "%c", opts->short_name); + else + pos += fprintf(stderr, "-%c", opts->short_name); + } if (opts->long_name && opts->short_name) pos += fprintf(stderr, ", "); if (opts->long_name) pos += fprintf(stderr, "--%s", opts->long_name); + if (opts->type == OPTION_NUMBER) + pos += fprintf(stderr, "-NUM"); switch (opts->type) { case OPTION_ARGUMENT: @@ -420,16 +540,12 @@ int usage_with_options_internal(const char * const *usagestr, if (opts->flags & PARSE_OPT_NOARG) break; /* FALLTHROUGH */ + case OPTION_FILENAME: + /* FALLTHROUGH */ case OPTION_STRING: - if (opts->argh) { - if (opts->flags & PARSE_OPT_OPTARG) - if (opts->long_name) - pos += fprintf(stderr, "[=<%s>]", opts->argh); - else - pos += fprintf(stderr, "[<%s>]", opts->argh); - else - pos += fprintf(stderr, " <%s>", opts->argh); - } else { + if (opts->argh) + pos += usage_argh(opts); + else { if (opts->flags & PARSE_OPT_OPTARG) if (opts->long_name) pos += fprintf(stderr, "[=...]"); @@ -439,7 +555,7 @@ int usage_with_options_internal(const char * const *usagestr, pos += fprintf(stderr, " ..."); } break; - default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */ + default: /* OPTION_{BIT,BOOLEAN,NUMBER,SET_INT,SET_PTR} */ break; } @@ -536,15 +652,3 @@ int parse_opt_with_commit(const struct option *opt, const char *arg, int unset) commit_list_insert(commit, opt->value); return 0; } - -/* - * This should really be OPTION_FILENAME type as a part of - * parse_options that take prefix to do this while parsing. - */ -extern const char *parse_options_fix_filename(const char *prefix, const char *file) -{ - if (!file || !prefix || is_absolute_path(file) || !strcmp("-", file)) - return file; - return prefix_filename(prefix, strlen(prefix), file); -} - diff --git a/parse-options.h b/parse-options.h index b54eec128b..5653dbab87 100644 --- a/parse-options.h +++ b/parse-options.h @@ -6,8 +6,10 @@ enum parse_opt_type { OPTION_END, OPTION_ARGUMENT, OPTION_GROUP, + OPTION_NUMBER, /* options with no arguments */ OPTION_BIT, + OPTION_NEGBIT, OPTION_BOOLEAN, /* _INCR would have been a better name */ OPTION_SET_INT, OPTION_SET_PTR, @@ -15,6 +17,7 @@ enum parse_opt_type { OPTION_STRING, OPTION_INTEGER, OPTION_CALLBACK, + OPTION_FILENAME }; enum parse_opt_flags { @@ -31,6 +34,8 @@ enum parse_opt_option_flags { PARSE_OPT_NONEG = 4, PARSE_OPT_HIDDEN = 8, PARSE_OPT_LASTARG_DEFAULT = 16, + PARSE_OPT_NODASH = 32, + PARSE_OPT_LITERAL_ARGHELP = 64, }; struct option; @@ -64,8 +69,17 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset); * PARSE_OPT_OPTARG: says that the argument is optional (not for BOOLEANs) * PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs * PARSE_OPT_NONEG: says that this option cannot be negated - * PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in - * the long one. + * PARSE_OPT_HIDDEN: this option is skipped in the default usage, and + * shown only in the full usage. + * PARSE_OPT_LASTARG_DEFAULT: says that this option will take the default + * value if no argument is given when the option + * is last on the command line. If the option is + * not last it will require an argument. + * Should not be used with PARSE_OPT_OPTARG. + * PARSE_OPT_NODASH: this option doesn't start with a dash. + * PARSE_OPT_LITERAL_ARGHELP: says that argh shouldn't be enclosed in brackets + * (i.e. '<argh>') in the help message. + * Useful for options with multiple parameters. * * `callback`:: * pointer to the callback to use for OPTION_CALLBACK. @@ -93,6 +107,7 @@ struct option { #define OPT_ARGUMENT(l, h) { OPTION_ARGUMENT, 0, (l), NULL, NULL, (h) } #define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) } #define OPT_BIT(s, l, v, h, b) { OPTION_BIT, (s), (l), (v), NULL, (h), 0, NULL, (b) } +#define OPT_NEGBIT(s, l, v, h, b) { OPTION_NEGBIT, (s), (l), (v), NULL, (h), 0, NULL, (b) } #define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) } #define OPT_SET_INT(s, l, v, h, i) { OPTION_SET_INT, (s), (l), (v), NULL, (h), 0, NULL, (i) } #define OPT_SET_PTR(s, l, v, h, p) { OPTION_SET_PTR, (s), (l), (v), NULL, (h), 0, NULL, (p) } @@ -103,12 +118,17 @@ struct option { parse_opt_approxidate_cb } #define OPT_CALLBACK(s, l, v, a, h, f) \ { OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) } +#define OPT_NUMBER_CALLBACK(v, h, f) \ + { OPTION_NUMBER, 0, NULL, (v), NULL, (h), \ + PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) } +#define OPT_FILENAME(s, l, v, h) { OPTION_FILENAME, (s), (l), (v), \ + "FILE", (h) } /* parse_options() will filter out the processed options and leave the * non-option arguments in argv[]. * Returns the number of arguments left in argv[]. */ -extern int parse_options(int argc, const char **argv, +extern int parse_options(int argc, const char **argv, const char *prefix, const struct option *options, const char * const usagestr[], int flags); @@ -134,13 +154,15 @@ struct parse_opt_ctx_t { int argc, cpidx; const char *opt; int flags; + const char *prefix; }; extern int parse_options_usage(const char * const *usagestr, const struct option *opts); extern void parse_options_start(struct parse_opt_ctx_t *ctx, - int argc, const char **argv, int flags); + int argc, const char **argv, const char *prefix, + int flags); extern int parse_options_step(struct parse_opt_ctx_t *ctx, const struct option *options, @@ -168,6 +190,4 @@ extern int parse_opt_with_commit(const struct option *, const char *, int); "use <n> digits to display SHA-1s", \ PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } -extern const char *parse_options_fix_filename(const char *prefix, const char *file); - #endif @@ -139,6 +139,22 @@ int git_mkstemp(char *path, size_t len, const char *template) return mkstemp(path); } +/* git_mkstemps() - create tmp file with suffix honoring TMPDIR variable. */ +int git_mkstemps(char *path, size_t len, const char *template, int suffix_len) +{ + const char *tmp; + size_t n; + + tmp = getenv("TMPDIR"); + if (!tmp) + tmp = "/tmp"; + n = snprintf(path, len, "%s/%s", tmp, template); + if (len <= n) { + errno = ENAMETOOLONG; + return -1; + } + return mkstemps(path, suffix_len); +} int validate_headref(const char *path) { diff --git a/perl/Git.pm b/perl/Git.pm index 291ff5b53c..e8df55d2f2 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -185,7 +185,7 @@ sub repository { if ($dir) { $dir =~ m#^/# or $dir = $opts{Directory} . '/' . $dir; - $opts{Repository} = $dir; + $opts{Repository} = abs_path($dir); # If --git-dir went ok, this shouldn't die either. my $prefix = $search->command_oneline('rev-parse', '--show-prefix'); @@ -1280,6 +1280,8 @@ sub _cmd_exec { my ($self, @args) = @_; if ($self) { $self->repo_path() and $ENV{'GIT_DIR'} = $self->repo_path(); + $self->repo_path() and $self->wc_path() + and $ENV{'GIT_WORK_TREE'} = $self->wc_path(); $self->wc_path() and chdir($self->wc_path()); $self->wc_subdir() and chdir($self->wc_subdir()); } @@ -284,7 +284,7 @@ static char *replace_encoding_header(char *buf, const char *encoding) static char *logmsg_reencode(const struct commit *commit, const char *output_encoding) { - static const char *utf8 = "utf-8"; + static const char *utf8 = "UTF-8"; const char *use_encoding; char *encoding; char *out; @@ -881,7 +881,7 @@ char *reencode_commit_message(const struct commit *commit, const char **encoding ? git_log_output_encoding : git_commit_encoding); if (!encoding) - encoding = "utf-8"; + encoding = "UTF-8"; if (encoding_p) *encoding_p = encoding; return logmsg_reencode(commit, encoding); @@ -682,12 +682,13 @@ int for_each_rawref(each_ref_fn fn, void *cb_data) * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or * - it ends with a "/". * - it ends with ".lock" + * - it contains a "\" (backslash) */ static inline int bad_ref_char(int ch) { if (((unsigned) ch) <= ' ' || - ch == '~' || ch == '^' || ch == ':') + ch == '~' || ch == '^' || ch == ':' || ch == '\\') return 1; /* 2.13 Pattern Matching Notation */ if (ch == '?' || ch == '[') /* Unsupported */ @@ -750,9 +751,8 @@ int check_ref_format(const char *ref) } } -const char *prettify_ref(const struct ref *ref) +const char *prettify_refname(const char *name) { - const char *name = ref->name; return name + ( !prefixcmp(name, "refs/heads/") ? 11 : !prefixcmp(name, "refs/tags/") ? 10 : @@ -80,7 +80,7 @@ extern int for_each_reflog(each_ref_fn, void *); #define CHECK_REF_FORMAT_WILDCARD (-3) extern int check_ref_format(const char *target); -extern const char *prettify_ref(const struct ref *ref); +extern const char *prettify_refname(const char *refname); extern char *shorten_unambiguous_ref(const char *ref, int strict); /** rename ref, return 0 on success **/ @@ -1085,12 +1085,20 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, return NULL; } +static struct ref **tail_ref(struct ref **head) +{ + struct ref **tail = head; + while (*tail) + tail = &((*tail)->next); + return tail; +} + /* * Note. This is used only by "push"; refspec matching rules for * push and fetch are subtly different, so do not try to reuse it * without thinking. */ -int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, +int match_refs(struct ref *src, struct ref **dst, int nr_refspec, const char **refspec, int flags) { struct refspec *rs; @@ -1098,13 +1106,14 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, int send_mirror = flags & MATCH_REFS_MIRROR; int errs; static const char *default_refspec[] = { ":", 0 }; + struct ref **dst_tail = tail_ref(dst); if (!nr_refspec) { nr_refspec = 1; refspec = default_refspec; } rs = parse_push_refspec(nr_refspec, (const char **) refspec); - errs = match_explicit_refs(src, dst, dst_tail, rs, nr_refspec); + errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec); /* pick the remainder */ for ( ; src; src = src->next) { @@ -1134,7 +1143,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, dst_side, &dst_name)) die("Didn't think it matches any more"); } - dst_peer = find_ref_by_name(dst, dst_name); + dst_peer = find_ref_by_name(*dst, dst_name); if (dst_peer) { if (dst_peer->peer_ref) /* We're already sending something to this ref. */ @@ -1150,7 +1159,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, goto free_name; /* Create a new one and link it */ - dst_peer = make_linked_ref(dst_name, dst_tail); + dst_peer = make_linked_ref(dst_name, &dst_tail); hashcpy(dst_peer->new_sha1, src->new_sha1); } dst_peer->peer_ref = copy_ref(src); @@ -1399,13 +1408,13 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs) base = branch->merge[0]->dst; if (!resolve_ref(base, sha1, 1, NULL)) return 0; - theirs = lookup_commit(sha1); + theirs = lookup_commit_reference(sha1); if (!theirs) return 0; if (!resolve_ref(branch->refname, sha1, 1, NULL)) return 0; - ours = lookup_commit(sha1); + ours = lookup_commit_reference(sha1); if (!ours) return 0; @@ -85,7 +85,7 @@ void ref_remove_duplicates(struct ref *ref_map); int valid_fetch_refspec(const char *refspec); struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec); -int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, +int match_refs(struct ref *src, struct ref **dst, int nr_refspec, const char **refspec, int all); /* diff --git a/revision.c b/revision.c index 18b7ebbbd5..bf58448367 100644 --- a/revision.c +++ b/revision.c @@ -256,10 +256,12 @@ static int everybody_uninteresting(struct commit_list *orig) /* * The goal is to get REV_TREE_NEW as the result only if the - * diff consists of all '+' (and no other changes), and - * REV_TREE_DIFFERENT otherwise (of course if the trees are - * the same we want REV_TREE_SAME). That means that once we - * get to REV_TREE_DIFFERENT, we do not have to look any further. + * diff consists of all '+' (and no other changes), REV_TREE_OLD + * if the whole diff is removal of old data, and otherwise + * REV_TREE_DIFFERENT (of course if the trees are the same we + * want REV_TREE_SAME). + * That means that once we get to REV_TREE_DIFFERENT, we do not + * have to look any further. */ static int tree_difference = REV_TREE_SAME; @@ -268,22 +270,9 @@ static void file_add_remove(struct diff_options *options, const unsigned char *sha1, const char *fullpath) { - int diff = REV_TREE_DIFFERENT; + int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD; - /* - * Is it an add of a new file? It means that the old tree - * didn't have it at all, so we will turn "REV_TREE_SAME" -> - * "REV_TREE_NEW", but leave any "REV_TREE_DIFFERENT" alone - * (and if it already was "REV_TREE_NEW", we'll keep it - * "REV_TREE_NEW" of course). - */ - if (addremove == '+') { - diff = tree_difference; - if (diff != REV_TREE_SAME) - return; - diff = REV_TREE_NEW; - } - tree_difference = diff; + tree_difference |= diff; if (tree_difference == REV_TREE_DIFFERENT) DIFF_OPT_SET(options, HAS_CHANGES); } @@ -305,6 +294,8 @@ static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct if (!t1) return REV_TREE_NEW; + if (!t2) + return REV_TREE_OLD; if (revs->simplify_by_decoration) { /* @@ -323,8 +314,7 @@ static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct if (!revs->prune_data) return REV_TREE_SAME; } - if (!t2) - return REV_TREE_DIFFERENT; + tree_difference = REV_TREE_SAME; DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES); if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "", @@ -429,6 +419,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit) p->parents = NULL; } /* fallthrough */ + case REV_TREE_OLD: case REV_TREE_DIFFERENT: tree_changed = 1; pp = &parent->next; diff --git a/revision.h b/revision.h index be39e7d386..227164cf70 100644 --- a/revision.h +++ b/revision.h @@ -118,8 +118,9 @@ struct rev_info { }; #define REV_TREE_SAME 0 -#define REV_TREE_NEW 1 -#define REV_TREE_DIFFERENT 2 +#define REV_TREE_NEW 1 /* Only new files */ +#define REV_TREE_OLD 2 /* Only files removed */ +#define REV_TREE_DIFFERENT 3 /* Mixed changes */ /* revision.c */ void read_revisions_from_stdin(struct rev_info *revs); diff --git a/symlinks.c b/symlinks.c index 1d6b35b858..8dcd63261f 100644 --- a/symlinks.c +++ b/symlinks.c @@ -196,8 +196,9 @@ void invalidate_lstat_cache(const char *name, int len) cache.path[previous_slash] = '\0'; cache.len = previous_slash; cache.flags = FL_DIR; - } else + } else { reset_lstat_cache(); + } } } @@ -263,7 +264,6 @@ static void do_remove_scheduled_dirs(int new_len) removal.path[removal.len] != '/'); } removal.len = new_len; - return; } void schedule_dir_for_removal(const char *name, int len) @@ -296,11 +296,9 @@ void schedule_dir_for_removal(const char *name, int len) last_slash - match_len); removal.len = last_slash; } - return; } void remove_scheduled_dirs(void) { do_remove_scheduled_dirs(0); - return; } diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh index 773d47cf3c..5654962343 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -26,6 +26,8 @@ fi svnrepo=$PWD/svnrepo export svnrepo +svnconf=$PWD/svnconf +export svnconf perl -w -e " use SVN::Core; @@ -54,6 +56,19 @@ poke() { test-chmtime +1 "$1" } +# We need this, because we should pass empty configuration directory to +# the 'svn commit' to avoid automated property changes and other stuff +# that could be set from user's configuration files in ~/.subversion. +svn_cmd () { + [ -d "$svnconf" ] || mkdir "$svnconf" + orig_svncmd="$1"; shift + if [ -z "$orig_svncmd" ]; then + svn + return + fi + svn "$orig_svncmd" --config-dir "$svnconf" "$@" +} + for d in \ "$SVN_HTTPD_PATH" \ /usr/sbin/apache2 \ diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index cde659d14a..6765b08065 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -93,14 +93,16 @@ prepare_httpd() { start_httpd() { prepare_httpd >&3 2>&4 - trap 'stop_httpd; die' EXIT + trap 'code=$?; stop_httpd; (exit $code); die' EXIT "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \ -f "$TEST_PATH/apache.conf" $HTTPD_PARA \ -c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \ >&3 2>&4 - if ! test $? = 0; then + if test $? -ne 0 + then say "skipping test, web server setup failed" + trap 'die' EXIT test_done fi } diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index e38241c80a..bbc821ef97 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -12,12 +12,14 @@ usage: test-parse-options <options> -b, --boolean get a boolean -4, --or4 bitwise-or boolean with ...0100 + --neg-or4 same as --no-or4 -i, --integer <n> get a integer -j <n> get a integer, too --set23 set integer to 23 -t <time> get timestamp of <time> -L, --length <str> get length of <str> + -F, --file <FILE> set file to <FILE> String options -s, --string <string> @@ -29,6 +31,8 @@ String options Magic arguments --quux means --quux + -NUM set integer to NUM + + same as -b Standard options --abbrev[=<n>] use <n> digits to display SHA-1s @@ -53,10 +57,12 @@ abbrev: 7 verbose: 2 quiet: no dry run: yes +file: prefix/my.file EOF test_expect_success 'short options' ' - test-parse-options -s123 -b -i 1729 -b -vv -n > output 2> output.err && + test-parse-options -s123 -b -i 1729 -b -vv -n -F my.file \ + > output 2> output.err && test_cmp expect output && test ! -s output.err ' @@ -70,11 +76,12 @@ abbrev: 10 verbose: 2 quiet: no dry run: no +file: prefix/fi.le EOF test_expect_success 'long options' ' test-parse-options --boolean --integer 1729 --boolean --string2=321 \ - --verbose --verbose --no-dry-run --abbrev=10 \ + --verbose --verbose --no-dry-run --abbrev=10 --file fi.le\ > output 2> output.err && test ! -s output.err && test_cmp expect output @@ -84,6 +91,8 @@ test_expect_success 'missing required value' ' test-parse-options -s; test $? = 129 && test-parse-options --string; + test $? = 129 && + test-parse-options --file; test $? = 129 ' @@ -96,6 +105,7 @@ abbrev: 7 verbose: 0 quiet: no dry run: no +file: (not set) arg 00: a1 arg 01: b1 arg 02: --boolean @@ -117,6 +127,7 @@ abbrev: 7 verbose: 0 quiet: no dry run: no +file: (not set) EOF test_expect_success 'unambiguously abbreviated option' ' @@ -145,6 +156,7 @@ abbrev: 7 verbose: 0 quiet: no dry run: no +file: (not set) EOF test_expect_success 'non ambiguous option (after two options it abbreviates)' ' @@ -172,6 +184,7 @@ abbrev: 7 verbose: 0 quiet: no dry run: no +file: (not set) arg 00: --quux EOF @@ -190,6 +203,7 @@ abbrev: 7 verbose: 0 quiet: yes dry run: no +file: (not set) arg 00: foo EOF @@ -210,6 +224,7 @@ abbrev: 7 verbose: 0 quiet: no dry run: no +file: (not set) EOF test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' ' @@ -237,6 +252,7 @@ abbrev: 7 verbose: 0 quiet: no dry run: no +file: (not set) EOF test_expect_success 'OPT_BIT() and OPT_SET_INT() work' ' @@ -245,7 +261,58 @@ test_expect_success 'OPT_BIT() and OPT_SET_INT() work' ' test_cmp expect output ' -# --or4 -# --no-or4 +test_expect_success 'OPT_NEGBIT() and OPT_SET_INT() work' ' + test-parse-options --set23 -bbbbb --neg-or4 > output 2> output.err && + test ! -s output.err && + test_cmp expect output +' + +cat > expect <<EOF +boolean: 6 +integer: 0 +timestamp: 0 +string: (not set) +abbrev: 7 +verbose: 0 +quiet: no +dry run: no +file: (not set) +EOF + +test_expect_success 'OPT_BIT() works' ' + test-parse-options -bb --or4 > output 2> output.err && + test ! -s output.err && + test_cmp expect output +' + +test_expect_success 'OPT_NEGBIT() works' ' + test-parse-options -bb --no-neg-or4 > output 2> output.err && + test ! -s output.err && + test_cmp expect output +' + +test_expect_success 'OPT_BOOLEAN() with PARSE_OPT_NODASH works' ' + test-parse-options + + + + + + > output 2> output.err && + test ! -s output.err && + test_cmp expect output +' + +cat > expect <<EOF +boolean: 0 +integer: 12345 +timestamp: 0 +string: (not set) +abbrev: 7 +verbose: 0 +quiet: no +dry run: no +file: (not set) +EOF + +test_expect_success 'OPT_NUMBER_CALLBACK() works' ' + test-parse-options -12345 > output 2> output.err && + test ! -s output.err && + test_cmp expect output +' test_done diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh new file mode 100755 index 0000000000..9956e3ad62 --- /dev/null +++ b/t/t1010-mktree.sh @@ -0,0 +1,71 @@ +#!/bin/sh + +test_description='git mktree' + +. ./test-lib.sh + +test_expect_success setup ' + for d in a a. a0 + do + mkdir "$d" && echo "$d/one" >"$d/one" && + git add "$d" + done && + echo zero >one && + git update-index --add --info-only one && + git write-tree --missing-ok >tree.missing && + git ls-tree $(cat tree.missing) >top.missing && + git ls-tree -r $(cat tree.missing) >all.missing && + echo one >one && + git add one && + git write-tree >tree && + git ls-tree $(cat tree) >top && + git ls-tree -r $(cat tree) >all && + test_tick && + git commit -q -m one && + H=$(git rev-parse HEAD) && + git update-index --add --cacheinfo 160000 $H sub && + test_tick && + git commit -q -m two && + git rev-parse HEAD^{tree} >tree.withsub && + git ls-tree HEAD >top.withsub && + git ls-tree -r HEAD >all.withsub +' + +test_expect_success 'ls-tree piped to mktree (1)' ' + git mktree <top >actual && + test_cmp tree actual +' + +test_expect_success 'ls-tree piped to mktree (2)' ' + git mktree <top.withsub >actual && + test_cmp tree.withsub actual +' + +test_expect_success 'ls-tree output in wrong order given to mktree (1)' ' + perl -e "print reverse <>" <top | + git mktree >actual && + test_cmp tree actual +' + +test_expect_success 'ls-tree output in wrong order given to mktree (2)' ' + perl -e "print reverse <>" <top.withsub | + git mktree >actual && + test_cmp tree.withsub actual +' + +test_expect_success 'allow missing object with --missing' ' + git mktree --missing <top.missing >actual && + test_cmp tree.missing actual +' + +test_expect_failure 'mktree reads ls-tree -r output (1)' ' + git mktree <all >actual && + test_cmp tree actual +' + +test_expect_failure 'mktree reads ls-tree -r output (2)' ' + git mktree <all.withsub >actual && + test_cmp tree.withsub actual +' + +test_done diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index 6e391a3702..7f62bfb9dd 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -41,9 +41,40 @@ test_expect_success \ git tag topic ' +test_expect_success 'rebase on dirty worktree' ' + echo dirty >> A && + test_must_fail git rebase master' + +test_expect_success 'rebase on dirty cache' ' + git add A && + test_must_fail git rebase master' + test_expect_success 'rebase against master' ' + git reset --hard HEAD && git rebase master' +test_expect_success 'rebase against master twice' ' + git rebase master 2>err && + grep "Current branch my-topic-branch is up to date" err +' + +test_expect_success 'rebase against master twice with --force' ' + git rebase --force-rebase master >out && + grep "Current branch my-topic-branch is up to date, rebase forced" out +' + +test_expect_success 'rebase against master twice from another branch' ' + git checkout my-topic-branch^ && + git rebase master my-topic-branch 2>err && + grep "Current branch my-topic-branch is up to date" err +' + +test_expect_success 'rebase fast-forward to master' ' + git checkout my-topic-branch^ && + git rebase my-topic-branch 2>err && + grep "Fast-forwarded HEAD to my-topic-branch" err +' + test_expect_success \ 'the rebase operation should not have destroyed author information' \ '! (git log | grep "Author:" | grep "<>")' diff --git a/t/t3700-add.sh b/t/t3700-add.sh index 050de42ef4..6ce8256a17 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -230,4 +230,16 @@ test_expect_success BSLASHPSPEC "git add 'fo\\[ou\\]bar' ignores foobar" ' ! ( git ls-files foobar | grep foobar ) ' +test_expect_success 'git add to resolve conflicts on otherwise ignored path' ' + git reset --hard && + H=$(git rev-parse :1/2/a) && + ( + echo "100644 $H 1 track-this" + echo "100644 $H 3 track-this" + ) | git update-index --index-info && + echo track-this >>.gitignore && + echo resolved >track-this && + git add track-this +' + test_done diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index dfc65601aa..fd2a55a5c2 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -165,4 +165,42 @@ test_expect_success FILEMODE 'stage mode but not hunk' ' # end of tests disabled when filemode is not usable +test_expect_success 'setup again' ' + git reset --hard && + test_chmod +x file && + echo content >>file +' + +# Write the patch file with a new line at the top and bottom +cat >patch <<EOF +index 180b47c..b6f2c08 100644 +--- a/file ++++ b/file +@@ -1,2 +1,4 @@ ++firstline + baseline + content ++lastline +EOF +# Expected output, similar to the patch but w/ diff at the top +cat >expected <<EOF +diff --git a/file b/file +index b6f2c08..61b9053 100755 +--- a/file ++++ b/file +@@ -1,2 +1,4 @@ ++firstline + baseline + content ++lastline +EOF +# Test splitting the first patch, then adding both +test_expect_success 'add first line works' ' + git commit -am "clear local changes" && + git apply patch && + (echo s; echo y; echo y) | git add -p file && + git diff --cached > diff && + test_cmp expected diff +' + test_done diff --git a/t/t3702-add-edit.sh b/t/t3702-add-edit.sh new file mode 100755 index 0000000000..4ee47cc9a8 --- /dev/null +++ b/t/t3702-add-edit.sh @@ -0,0 +1,121 @@ +#!/bin/sh +# +# Copyright (c) 2007 Johannes E. Schindelin +# + +test_description='add -e basic tests' +. ./test-lib.sh + + +cat > file << EOF +LO, praise of the prowess of people-kings +of spear-armed Danes, in days long sped, +we have heard, and what honor the athelings won! +Oft Scyld the Scefing from squadroned foes, +from many a tribe, the mead-bench tore, +awing the earls. Since erst he lay +friendless, a foundling, fate repaid him: +for he waxed under welkin, in wealth he throve, +till before him the folk, both far and near, +who house by the whale-path, heard his mandate, +gave him gifts: a good king he! +EOF + +cat > second-part << EOF +To him an heir was afterward born, +a son in his halls, whom heaven sent +to favor the folk, feeling their woe +that erst they had lacked an earl for leader +so long a while; the Lord endowed him, +the Wielder of Wonder, with world's renown. +EOF + +test_expect_success 'setup' ' + + git add file && + test_tick && + git commit -m initial file + +' + +cat > expected-patch << EOF +diff --git a/file b/file +index b9834b5..9020acb 100644 +--- a/file ++++ b/file +@@ -1,11 +1,6 @@ +-LO, praise of the prowess of people-kings +-of spear-armed Danes, in days long sped, +-we have heard, and what honor the athelings won! +-Oft Scyld the Scefing from squadroned foes, +-from many a tribe, the mead-bench tore, +-awing the earls. Since erst he lay +-friendless, a foundling, fate repaid him: +-for he waxed under welkin, in wealth he throve, +-till before him the folk, both far and near, +-who house by the whale-path, heard his mandate, +-gave him gifts: a good king he! ++To him an heir was afterward born, ++a son in his halls, whom heaven sent ++to favor the folk, feeling their woe ++that erst they had lacked an earl for leader ++so long a while; the Lord endowed him, ++the Wielder of Wonder, with world's renown. +EOF + +cat > patch << EOF +diff --git a/file b/file +index b9834b5..ef6e94c 100644 +--- a/file ++++ b/file +@@ -3,1 +3,333 @@ of spear-armed Danes, in days long sped, + we have heard, and what honor the athelings won! ++ + Oft Scyld the Scefing from squadroned foes, +@@ -2,7 +1,5 @@ awing the earls. Since erst he lay + friendless, a foundling, fate repaid him: ++ + for he waxed under welkin, in wealth he throve, +EOF + +cat > expected << EOF +diff --git a/file b/file +index b9834b5..ef6e94c 100644 +--- a/file ++++ b/file +@@ -1,10 +1,12 @@ + LO, praise of the prowess of people-kings + of spear-armed Danes, in days long sped, + we have heard, and what honor the athelings won! ++ + Oft Scyld the Scefing from squadroned foes, + from many a tribe, the mead-bench tore, + awing the earls. Since erst he lay + friendless, a foundling, fate repaid him: ++ + for he waxed under welkin, in wealth he throve, + till before him the folk, both far and near, + who house by the whale-path, heard his mandate, +EOF + +echo "#!$SHELL_PATH" >fake-editor.sh +cat >> fake-editor.sh <<\EOF +mv -f "$1" orig-patch && +mv -f patch "$1" +EOF + +test_set_editor "$(pwd)/fake-editor.sh" +chmod a+x fake-editor.sh + +test_expect_success 'add -e' ' + + cp second-part file && + git add -e && + test_cmp second-part file && + test_cmp orig-patch expected-patch && + git diff --cached > out && + test_cmp out expected + +' + +test_done diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh index 784c31aec9..256c4c9701 100755 --- a/t/t3900-i18n-commit.sh +++ b/t/t3900-i18n-commit.sh @@ -9,7 +9,15 @@ test_description='commit and log output encodings' compare_with () { git show -s $1 | sed -e '1,/^$/d' -e 's/^ //' >current && - test_cmp current "$2" + case "$3" in + '') + test_cmp "$2" current ;; + ?*) + iconv -f "$3" -t UTF-8 >current.utf8 <current && + iconv -f "$3" -t UTF-8 >expect.utf8 <"$2" && + test_cmp expect.utf8 current.utf8 + ;; + esac } test_expect_success setup ' @@ -26,7 +34,7 @@ test_expect_success 'no encoding header for base case' ' test z = "z$E" ' -for H in ISO-8859-1 EUCJP ISO-2022-JP +for H in ISO8859-1 eucJP ISO-2022-JP do test_expect_success "$H setup" ' git config i18n.commitencoding $H && @@ -36,7 +44,7 @@ do ' done -for H in ISO-8859-1 EUCJP ISO-2022-JP +for H in ISO8859-1 eucJP ISO-2022-JP do test_expect_success "check encoding header for $H" ' E=$(git cat-file commit '$H' | sed -ne "s/^encoding //p") && @@ -53,14 +61,14 @@ test_expect_success 'config to remove customization' ' else test z = "z$Z" fi && - git config i18n.commitencoding utf-8 + git config i18n.commitencoding UTF-8 ' -test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' ' - compare_with ISO-8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt +test_expect_success 'ISO8859-1 should be shown in UTF-8 now' ' + compare_with ISO8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt ' -for H in EUCJP ISO-2022-JP +for H in eucJP ISO-2022-JP do test_expect_success "$H should be shown in UTF-8 now" ' compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt @@ -78,7 +86,7 @@ test_expect_success 'config to add customization' ' fi ' -for H in ISO-8859-1 EUCJP ISO-2022-JP +for H in ISO8859-1 eucJP ISO-2022-JP do test_expect_success "$H should be shown in itself now" ' git config i18n.commitencoding '$H' && @@ -87,32 +95,38 @@ do done test_expect_success 'config to tweak customization' ' - git config i18n.logoutputencoding utf-8 + git config i18n.logoutputencoding UTF-8 ' -test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' ' - compare_with ISO-8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt +test_expect_success 'ISO8859-1 should be shown in UTF-8 now' ' + compare_with ISO8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt ' -for H in EUCJP ISO-2022-JP +for H in eucJP ISO-2022-JP do test_expect_success "$H should be shown in UTF-8 now" ' compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt ' done -for J in EUCJP ISO-2022-JP +for J in eucJP ISO-2022-JP do + if test "$J" = ISO-2022-JP + then + ICONV=$J + else + ICONV= + fi git config i18n.logoutputencoding $J - for H in EUCJP ISO-2022-JP + for H in eucJP ISO-2022-JP do test_expect_success "$H should be shown in $J now" ' - compare_with '$H' "$TEST_DIRECTORY"/t3900/'$J'.txt + compare_with '$H' "$TEST_DIRECTORY"/t3900/'$J'.txt $ICONV ' done done -for H in ISO-8859-1 EUCJP ISO-2022-JP +for H in ISO8859-1 eucJP ISO-2022-JP do test_expect_success "No conversion with $H" ' compare_with "--encoding=none '$H'" "$TEST_DIRECTORY"/t3900/'$H'.txt diff --git a/t/t3900/ISO-8859-1.txt b/t/t3900/ISO8859-1.txt index 7cbef0ee6f..7cbef0ee6f 100644 --- a/t/t3900/ISO-8859-1.txt +++ b/t/t3900/ISO8859-1.txt diff --git a/t/t3900/EUCJP.txt b/t/t3900/eucJP.txt index 546f2aac01..546f2aac01 100644 --- a/t/t3900/EUCJP.txt +++ b/t/t3900/eucJP.txt diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh index 7655da3f8d..31a5770b34 100755 --- a/t/t3901-i18n-patch.sh +++ b/t/t3901-i18n-patch.sh @@ -17,9 +17,9 @@ check_encoding () { git cat-file commit HEAD~$j | case "$header" in 8859) - grep "^encoding ISO-8859-1" ;; + grep "^encoding ISO8859-1" ;; *) - ! grep "^encoding ISO-8859-1" ;; + grep "^encoding ISO8859-1"; test "$?" != 0 ;; esac || { bad=1 break @@ -55,7 +55,7 @@ test_expect_success setup ' git commit -s -m "Second on side" && # the second one on the side branch is ISO-8859-1 - git config i18n.commitencoding ISO-8859-1 && + git config i18n.commitencoding ISO8859-1 && # use author and committer name in ISO-8859-1 to match it. . "$TEST_DIRECTORY"/t3901-8859-1.txt && test_tick && @@ -68,14 +68,14 @@ test_expect_success setup ' ' test_expect_success 'format-patch output (ISO-8859-1)' ' - git config i18n.logoutputencoding ISO-8859-1 && + git config i18n.logoutputencoding ISO8859-1 && git format-patch --stdout master..HEAD^ >out-l1 && git format-patch --stdout HEAD^ >out-l2 && - grep "^Content-Type: text/plain; charset=ISO-8859-1" out-l1 && - grep "^From: =?ISO-8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l1 && - grep "^Content-Type: text/plain; charset=ISO-8859-1" out-l2 && - grep "^From: =?ISO-8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l2 + grep "^Content-Type: text/plain; charset=ISO8859-1" out-l1 && + grep "^From: =?ISO8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l1 && + grep "^Content-Type: text/plain; charset=ISO8859-1" out-l2 && + grep "^From: =?ISO8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l2 ' test_expect_success 'format-patch output (UTF-8)' ' @@ -110,7 +110,7 @@ test_expect_success 'rebase (U/U)' ' test_expect_success 'rebase (U/L)' ' git config i18n.commitencoding UTF-8 && - git config i18n.logoutputencoding ISO-8859-1 && + git config i18n.logoutputencoding ISO8859-1 && . "$TEST_DIRECTORY"/t3901-utf8.txt && git reset --hard side && @@ -121,8 +121,8 @@ test_expect_success 'rebase (U/L)' ' test_expect_success 'rebase (L/L)' ' # In this test we want ISO-8859-1 encoded commits as the result - git config i18n.commitencoding ISO-8859-1 && - git config i18n.logoutputencoding ISO-8859-1 && + git config i18n.commitencoding ISO8859-1 && + git config i18n.logoutputencoding ISO8859-1 && . "$TEST_DIRECTORY"/t3901-8859-1.txt && git reset --hard side && @@ -134,7 +134,7 @@ test_expect_success 'rebase (L/L)' ' test_expect_success 'rebase (L/U)' ' # This is pathological -- use UTF-8 as intermediate form # to get ISO-8859-1 results. - git config i18n.commitencoding ISO-8859-1 && + git config i18n.commitencoding ISO8859-1 && git config i18n.logoutputencoding UTF-8 && . "$TEST_DIRECTORY"/t3901-8859-1.txt && @@ -162,8 +162,8 @@ test_expect_success 'cherry-pick(U/U)' ' test_expect_success 'cherry-pick(L/L)' ' # Both the commitencoding and logoutputencoding is set to ISO-8859-1 - git config i18n.commitencoding ISO-8859-1 && - git config i18n.logoutputencoding ISO-8859-1 && + git config i18n.commitencoding ISO8859-1 && + git config i18n.logoutputencoding ISO8859-1 && . "$TEST_DIRECTORY"/t3901-8859-1.txt && git reset --hard master && @@ -178,7 +178,7 @@ test_expect_success 'cherry-pick(U/L)' ' # Commitencoding is set to UTF-8 but logoutputencoding is ISO-8859-1 git config i18n.commitencoding UTF-8 && - git config i18n.logoutputencoding ISO-8859-1 && + git config i18n.logoutputencoding ISO8859-1 && . "$TEST_DIRECTORY"/t3901-utf8.txt && git reset --hard master && @@ -193,7 +193,7 @@ test_expect_success 'cherry-pick(L/U)' ' # Again, the commitencoding is set to ISO-8859-1 but # logoutputencoding is set to UTF-8. - git config i18n.commitencoding ISO-8859-1 && + git config i18n.commitencoding ISO8859-1 && git config i18n.logoutputencoding UTF-8 && . "$TEST_DIRECTORY"/t3901-8859-1.txt && @@ -218,7 +218,7 @@ test_expect_success 'rebase --merge (U/U)' ' test_expect_success 'rebase --merge (U/L)' ' git config i18n.commitencoding UTF-8 && - git config i18n.logoutputencoding ISO-8859-1 && + git config i18n.logoutputencoding ISO8859-1 && . "$TEST_DIRECTORY"/t3901-utf8.txt && git reset --hard side && @@ -229,8 +229,8 @@ test_expect_success 'rebase --merge (U/L)' ' test_expect_success 'rebase --merge (L/L)' ' # In this test we want ISO-8859-1 encoded commits as the result - git config i18n.commitencoding ISO-8859-1 && - git config i18n.logoutputencoding ISO-8859-1 && + git config i18n.commitencoding ISO8859-1 && + git config i18n.logoutputencoding ISO8859-1 && . "$TEST_DIRECTORY"/t3901-8859-1.txt && git reset --hard side && @@ -242,7 +242,7 @@ test_expect_success 'rebase --merge (L/L)' ' test_expect_success 'rebase --merge (L/U)' ' # This is pathological -- use UTF-8 as intermediate form # to get ISO-8859-1 results. - git config i18n.commitencoding ISO-8859-1 && + git config i18n.commitencoding ISO8859-1 && git config i18n.logoutputencoding UTF-8 && . "$TEST_DIRECTORY"/t3901-8859-1.txt && diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all index 12da8ac07d..954210ea90 100644 --- a/t/t4013/diff.log_--decorate_--all +++ b/t/t4013/diff.log_--decorate_--all @@ -1,12 +1,12 @@ $ git log --decorate --all -commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (refs/heads/master) +commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (master) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 Merge branch 'side' -commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side) +commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (side) Author: A U Thor <author@example.com> Date: Mon Jun 26 00:03:00 2006 +0000 @@ -26,7 +26,7 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. -commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial) +commit 444ac553ac7612cc88969031b02b3767fb8a353a (initial) Author: A U Thor <author@example.com> Date: Mon Jun 26 00:00:00 2006 +0000 diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index 0720001281..4ea42e00da 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -136,6 +136,15 @@ test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' ' GIT_EXTERNAL_DIFF=echo git diff ' +test_expect_success 'GIT_EXTERNAL_DIFF generates pretty paths' ' + touch file.ext && + git add file.ext && + echo with extension > file.ext && + GIT_EXTERNAL_DIFF=echo git diff file.ext | grep ......_file\.ext && + git update-index --force-remove file.ext && + rm file.ext +' + echo "#!$SHELL_PATH" >fake-diff.sh cat >> fake-diff.sh <<\EOF cat $2 >> crlfed.txt diff --git a/t/t4131-apply-fake-ancestor.sh b/t/t4131-apply-fake-ancestor.sh new file mode 100755 index 0000000000..94373ca9a0 --- /dev/null +++ b/t/t4131-apply-fake-ancestor.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# +# Copyright (c) 2009 Stephen Boyd +# + +test_description='git apply --build-fake-ancestor handling.' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit 1 && + test_commit 2 && + mkdir sub && + test_commit 3 sub/3 && + test_commit 4 +' + +test_expect_success 'apply --build-fake-ancestor' ' + git checkout 2 && + echo "A" > 1.t && + git diff > 1.patch && + git reset --hard && + git checkout 1 && + git apply --build-fake-ancestor 1.ancestor 1.patch +' + +test_expect_success 'apply --build-fake-ancestor in a subdirectory' ' + git checkout 3 && + echo "C" > sub/3.t && + git diff > 3.patch && + git reset --hard && + git checkout 4 && + ( + cd sub && + git apply --build-fake-ancestor 3.ancestor ../3.patch && + test -f 3.ancestor + ) && + git apply --build-fake-ancestor 3.ancestor 3.patch && + test_cmp sub/3.ancestor 3.ancestor +' + +test_done diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 64502e2be7..aad3894ad4 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -324,14 +324,12 @@ cat > expect <<\EOF * | | | Merge branch 'side' |\ \ \ \ | * | | | side-2 -| | | |/ -| | |/| +| | |_|/ | |/| | | * | | side-1 * | | | Second * | | | sixth -| | |/ -| |/| +| |_|/ |/| | * | | fifth * | | fourth diff --git a/t/t5100/rfc2047-samples.mbox b/t/t5100/rfc2047-samples.mbox index 3ca2470da2..1fc224810d 100644 --- a/t/t5100/rfc2047-samples.mbox +++ b/t/t5100/rfc2047-samples.mbox @@ -1,48 +1,48 @@ From nobody Mon Sep 17 00:00:00 2001 From: =?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu> -To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk> -CC: =?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be> -Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?= - =?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?= +To: =?ISO8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk> +CC: =?ISO8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be> +Subject: =?ISO8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?= + =?ISO8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?= From nobody Mon Sep 17 00:00:00 2001 -From: =?ISO-8859-1?Q?Olle_J=E4rnefors?= <ojarnef@admin.kth.se> +From: =?ISO8859-1?Q?Olle_J=E4rnefors?= <ojarnef@admin.kth.se> To: ietf-822@dimacs.rutgers.edu, ojarnef@admin.kth.se Subject: Time for ISO 10646? From nobody Mon Sep 17 00:00:00 2001 To: Dave Crocker <dcrocker@mordor.stanford.edu> Cc: ietf-822@dimacs.rutgers.edu, paf@comsol.se -From: =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@nada.kth.se> +From: =?ISO8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@nada.kth.se> Subject: Re: RFC-HDR care and feeding From nobody Mon Sep 17 00:00:00 2001 From: Nathaniel Borenstein <nsb@thumper.bellcore.com> - (=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?=) + (=?ISO8859-8?b?7eXs+SDv4SDp7Oj08A==?=) To: Greg Vaudreuil <gvaudre@NRI.Reston.VA.US>, Ned Freed <ned@innosoft.com>, Keith Moore <moore@cs.utk.edu> Subject: Test of new header generator MIME-Version: 1.0 -Content-type: text/plain; charset=ISO-8859-1 +Content-type: text/plain; charset=ISO8859-1 From nobody Mon Sep 17 00:00:00 2001 -Subject: (=?ISO-8859-1?Q?a?=) +Subject: (=?ISO8859-1?Q?a?=) From nobody Mon Sep 17 00:00:00 2001 -Subject: (=?ISO-8859-1?Q?a?= b) +Subject: (=?ISO8859-1?Q?a?= b) From nobody Mon Sep 17 00:00:00 2001 -Subject: (=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=) +Subject: (=?ISO8859-1?Q?a?= =?ISO8859-1?Q?b?=) From nobody Mon Sep 17 00:00:00 2001 -Subject: (=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=) +Subject: (=?ISO8859-1?Q?a?= =?ISO8859-1?Q?b?=) From nobody Mon Sep 17 00:00:00 2001 -Subject: (=?ISO-8859-1?Q?a?= - =?ISO-8859-1?Q?b?=) +Subject: (=?ISO8859-1?Q?a?= + =?ISO8859-1?Q?b?=) From nobody Mon Sep 17 00:00:00 2001 -Subject: (=?ISO-8859-1?Q?a_b?=) +Subject: (=?ISO8859-1?Q?a_b?=) From nobody Mon Sep 17 00:00:00 2001 -Subject: (=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=) +Subject: (=?ISO8859-1?Q?a?= =?ISO8859-2?Q?_b?=) diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox index c5ad206b40..c3074ac573 100644 --- a/t/t5100/sample.mbox +++ b/t/t5100/sample.mbox @@ -99,7 +99,7 @@ index 9123cdc..918dcf8 100644 From nobody Sat Aug 27 23:07:49 2005 Path: news.gmane.org!not-for-mail Message-ID: <20050721.091036.01119516.yoshfuji@linux-ipv6.org> -From: YOSHIFUJI Hideaki / =?iso-2022-jp?B?GyRCNUhGIzFRTEAbKEI=?= +From: YOSHIFUJI Hideaki / =?ISO-2022-JP?B?GyRCNUhGIzFRTEAbKEI=?= <yoshfuji@linux-ipv6.org> Newsgroups: gmane.comp.version-control.git Subject: [PATCH 1/2] GIT: Try all addresses for given remote name @@ -218,7 +218,7 @@ GPG-FP : 9022 65EB 1ECF 3AD1 0BDF 80D8 4807 F894 E062 0EEA From nobody Sat Aug 27 23:07:49 2005 Path: news.gmane.org!not-for-mail Message-ID: <u5tacjjdpxq.fsf@lysator.liu.se> -From: =?iso-8859-1?Q?David_K=E5gedal?= <davidk@lysator.liu.se> +From: =?ISO8859-1?Q?David_K=E5gedal?= <davidk@lysator.liu.se> Newsgroups: gmane.comp.version-control.git Subject: [PATCH] Fixed two bugs in git-cvsimport-script. Date: Mon, 15 Aug 2005 20:18:25 +0200 @@ -226,7 +226,7 @@ Lines: 83 Approved: news@gmane.org NNTP-Posting-Host: main.gmane.org Mime-Version: 1.0 -Content-Type: text/plain; charset=iso-8859-1 +Content-Type: text/plain; charset=ISO8859-1 Content-Transfer-Encoding: QUOTED-PRINTABLE X-Trace: sea.gmane.org 1124130247 31839 80.91.229.2 (15 Aug 2005 18:24:07 GMT) X-Complaints-To: usenet@sea.gmane.org @@ -476,7 +476,7 @@ MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" --=-=-= -Content-Type: text/plain; charset=iso-8859-15 +Content-Type: text/plain; charset=ISO8859-15 Content-Transfer-Encoding: quoted-printable Here comes a commit log message, and diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index c450f33f33..a8c2ca2a78 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -3,9 +3,8 @@ # Copyright (c) 2005 Johannes Schindelin # -test_description='Testing multi_ack pack fetching +test_description='Testing multi_ack pack fetching' -' . ./test-lib.sh # Test fetch-pack/upload-pack pair. @@ -13,77 +12,60 @@ test_description='Testing multi_ack pack fetching # Some convenience functions add () { - name=$1 - text="$@" - branch=`echo $name | sed -e 's/^\(.\).*$/\1/'` - parents="" + name=$1 && + text="$@" && + branch=`echo $name | sed -e 's/^\(.\).*$/\1/'` && + parents="" && - shift + shift && while test $1; do - parents="$parents -p $1" + parents="$parents -p $1" && shift - done + done && - echo "$text" > test.txt - git update-index --add test.txt - tree=$(git write-tree) + echo "$text" > test.txt && + git update-index --add test.txt && + tree=$(git write-tree) && # make sure timestamps are in correct order - sec=$(($sec+1)) - commit=$(echo "$text" | GIT_AUTHOR_DATE=$sec \ - git commit-tree $tree $parents 2>>log2.txt) - eval "$name=$commit; export $name" - echo $commit > .git/refs/heads/$branch + test_tick && + commit=$(echo "$text" | git commit-tree $tree $parents) && + eval "$name=$commit; export $name" && + echo $commit > .git/refs/heads/$branch && eval ${branch}TIP=$commit } -count_objects () { - ls .git/objects/??/* 2>>log2.txt | wc -l | tr -d " " -} - -test_expect_object_count () { - message=$1 - count=$2 - - output="$(count_objects)" - test_expect_success \ - "new object count $message" \ - "test $count = $output" -} - pull_to_client () { - number=$1 - heads=$2 - count=$3 - no_strict_count_check=$4 - - cd client - test_expect_success "$number pull" \ - "git fetch-pack -k -v .. $heads" - case "$heads" in *A*) echo $ATIP > .git/refs/heads/A;; esac - case "$heads" in *B*) echo $BTIP > .git/refs/heads/B;; esac - git symbolic-ref HEAD refs/heads/`echo $heads | sed -e 's/^\(.\).*$/\1/'` - - test_expect_success "fsck" 'git fsck --full > fsck.txt 2>&1' - - test_expect_success 'check downloaded results' \ - 'mv .git/objects/pack/pack-* . && - p=`ls -1 pack-*.pack` && - git unpack-objects <$p && - git fsck --full' - - test_expect_success "new object count after $number pull" \ - 'idx=`echo pack-*.idx` && - pack_count=`git show-index <$idx | wc -l` && - test $pack_count = $count' - test -z "$pack_count" && pack_count=0 - if [ -z "$no_strict_count_check" ]; then - test_expect_success "minimal count" "test $count = $pack_count" - else - test $count != $pack_count && \ - echo "WARNING: $pack_count objects transmitted, only $count of which were needed" - fi - rm -f pack-* - cd .. + number=$1 && + heads=$2 && + count=$3 && + test_expect_success "$number pull" ' + ( + cd client && + git fetch-pack -k -v .. $heads && + + case "$heads" in + *A*) + echo $ATIP > .git/refs/heads/A;; + esac && + case "$heads" in *B*) + echo $BTIP > .git/refs/heads/B;; + esac && + git symbolic-ref HEAD refs/heads/`echo $heads \ + | sed -e "s/^\(.\).*$/\1/"` && + + git fsck --full && + + mv .git/objects/pack/pack-* . && + p=`ls -1 pack-*.pack` && + git unpack-objects <$p && + git fsck --full && + + idx=`echo pack-*.idx` && + pack_count=`git show-index <$idx | wc -l` && + test $pack_count = $count && + rm -f pack-* + ) + ' } # Here begins the actual testing @@ -94,89 +76,129 @@ pull_to_client () { # client pulls A20, B1. Then tracks only B. Then pulls A. -( +test_expect_success 'setup' ' mkdir client && - cd client && - git init 2>> log2.txt && - git config transfer.unpacklimit 0 -) - -add A1 - -prev=1; cur=2; while [ $cur -le 10 ]; do - add A$cur $(eval echo \$A$prev) - prev=$cur - cur=$(($cur+1)) -done - -add B1 $A1 - -echo $ATIP > .git/refs/heads/A -echo $BTIP > .git/refs/heads/B -git symbolic-ref HEAD refs/heads/B + ( + cd client && + git init && + git config transfer.unpacklimit 0 + ) && + add A1 && + prev=1 && + cur=2 && + while [ $cur -le 10 ]; do + add A$cur $(eval echo \$A$prev) && + prev=$cur && + cur=$(($cur+1)) + done && + add B1 $A1 + echo $ATIP > .git/refs/heads/A && + echo $BTIP > .git/refs/heads/B && + git symbolic-ref HEAD refs/heads/B +' pull_to_client 1st "B A" $((11*3)) -add A11 $A10 - -prev=1; cur=2; while [ $cur -le 65 ]; do - add B$cur $(eval echo \$B$prev) - prev=$cur - cur=$(($cur+1)) -done +test_expect_success 'post 1st pull setup' ' + add A11 $A10 && + prev=1 && + cur=2 && + while [ $cur -le 65 ]; do + add B$cur $(eval echo \$B$prev) && + prev=$cur && + cur=$(($cur+1)) + done +' pull_to_client 2nd "B" $((64*3)) -pull_to_client 3rd "A" $((1*3)) # old fails - -test_expect_success "clone shallow" 'git clone --depth 2 "file://$(pwd)/." shallow' +pull_to_client 3rd "A" $((1*3)) -(cd shallow; git count-objects -v) > count.shallow - -test_expect_success "clone shallow object count" \ - "test \"in-pack: 18\" = \"$(grep in-pack count.shallow)\"" - -count_output () { - sed -e '/^in-pack:/d' -e '/^packs:/d' -e '/^size-pack:/d' -e '/: 0$/d' "$1" -} +test_expect_success 'clone shallow' ' + git clone --depth 2 "file://$(pwd)/." shallow +' -test_expect_success "clone shallow object count (part 2)" ' - test -z "$(count_output count.shallow)" +test_expect_success 'clone shallow object count' ' + ( + cd shallow && + git count-objects -v + ) > count.shallow && + grep "^in-pack: 18" count.shallow ' -test_expect_success "fsck in shallow repo" \ - "(cd shallow; git fsck --full)" +test_expect_success 'clone shallow object count (part 2)' ' + sed -e "/^in-pack:/d" -e "/^packs:/d" -e "/^size-pack:/d" \ + -e "/: 0$/d" count.shallow > count_output && + ! test -s count_output +' -#test_done; exit +test_expect_success 'fsck in shallow repo' ' + ( + cd shallow && + git fsck --full + ) +' -add B66 $B65 -add B67 $B66 +test_expect_success 'add two more' ' + add B66 $B65 && + add B67 $B66 +' -test_expect_success "pull in shallow repo" \ - "(cd shallow; git pull .. B)" +test_expect_success 'pull in shallow repo' ' + ( + cd shallow && + git pull .. B + ) +' -(cd shallow; git count-objects -v) > count.shallow -test_expect_success "clone shallow object count" \ - "test \"count: 6\" = \"$(grep count count.shallow)\"" +test_expect_success 'clone shallow object count' ' + ( + cd shallow && + git count-objects -v + ) > count.shallow && + grep "^count: 6" count.shallow +' -add B68 $B67 -add B69 $B68 +test_expect_success 'add two more (part 2)' ' + add B68 $B67 && + add B69 $B68 +' -test_expect_success "deepening pull in shallow repo" \ - "(cd shallow; git pull --depth 4 .. B)" +test_expect_success 'deepening pull in shallow repo' ' + ( + cd shallow && + git pull --depth 4 .. B + ) +' -(cd shallow; git count-objects -v) > count.shallow -test_expect_success "clone shallow object count" \ - "test \"count: 12\" = \"$(grep count count.shallow)\"" +test_expect_success 'clone shallow object count' ' + ( + cd shallow && + git count-objects -v + ) > count.shallow && + grep "^count: 12" count.shallow +' -test_expect_success "deepening fetch in shallow repo" \ - "(cd shallow; git fetch --depth 4 .. A:A)" +test_expect_success 'deepening fetch in shallow repo' ' + ( + cd shallow && + git fetch --depth 4 .. A:A + ) +' -(cd shallow; git count-objects -v) > count.shallow -test_expect_success "clone shallow object count" \ - "test \"count: 18\" = \"$(grep count count.shallow)\"" +test_expect_success 'clone shallow object count' ' + ( + cd shallow && + git count-objects -v + ) > count.shallow && + grep "^count: 18" count.shallow +' -test_expect_success "pull in shallow repo with missing merge base" \ - "(cd shallow && test_must_fail git pull --depth 4 .. A)" +test_expect_success 'pull in shallow repo with missing merge base' ' + ( + cd shallow && + test_must_fail git pull --depth 4 .. A + ) +' test_done diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh index f8942bc890..7dcf391914 100755 --- a/t/t6023-merge-file.sh +++ b/t/t6023-merge-file.sh @@ -54,6 +54,12 @@ deduxit me super semitas jusitiae, EOF printf "propter nomen suum." >> new4.txt +test_expect_success 'merge with no changes' ' + cp orig.txt test.txt && + git merge-file test.txt orig.txt orig.txt && + test_cmp test.txt orig.txt +' + cp new1.txt test.txt test_expect_success "merge without conflict" \ "git merge-file test.txt orig.txt new2.txt" diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 54b7ea6505..5254b23512 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -482,28 +482,17 @@ test_expect_success 'good merge bases when good and bad are siblings' ' git bisect reset ' -check_trace() { - grep "$1" "$GIT_TRACE" | grep "\^$2" | grep "$3" >/dev/null -} - test_expect_success 'optimized merge base checks' ' - GIT_TRACE="$(pwd)/trace.log" && - export GIT_TRACE && git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt && grep "merge base must be tested" my_bisect_log.txt && grep "$HASH4" my_bisect_log.txt && - check_trace "rev-list" "$HASH7" "$SIDE_HASH7" && git bisect good > my_bisect_log2.txt && test -f ".git/BISECT_ANCESTORS_OK" && test "$HASH6" = $(git rev-parse --verify HEAD) && - : > "$GIT_TRACE" && git bisect bad > my_bisect_log3.txt && - test_must_fail check_trace "rev-list" "$HASH6" "$SIDE_HASH7" && git bisect good "$A_HASH" > my_bisect_log4.txt && grep "merge base must be tested" my_bisect_log4.txt && - test_must_fail test -f ".git/BISECT_ANCESTORS_OK" && - check_trace "rev-list" "$HASH6" "$A_HASH" && - unset GIT_TRACE + test_must_fail test -f ".git/BISECT_ANCESTORS_OK" ' # This creates another side branch called "parallel" with some files diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh index 3d6db4d386..00e1de9627 100755 --- a/t/t6040-tracking-info.sh +++ b/t/t6040-tracking-info.sh @@ -74,5 +74,19 @@ test_expect_success 'status' ' grep "have 1 and 1 different" actual ' +test_expect_success 'status when tracking lightweight tags' ' + git checkout master && + git tag light && + git branch --track lighttrack light >actual && + grep "set up to track" actual && + git checkout lighttrack +' +test_expect_success 'status when tracking annotated tags' ' + git checkout master && + git tag -m heavy heavy && + git branch --track heavytrack heavy >actual && + grep "set up to track" actual && + git checkout heavytrack +' test_done diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh index 2049ab6cf8..42f6fff373 100755 --- a/t/t6200-fmt-merge-msg.sh +++ b/t/t6200-fmt-merge-msg.sh @@ -208,4 +208,36 @@ test_expect_success 'merge-msg test #5-2' ' test_cmp expected actual ' +test_expect_success 'merge-msg -F' ' + + git config --unset-all merge.log + git config --unset-all merge.summary + git config merge.summary yes && + + git checkout master && + setdate && + git fetch . left right && + + git fmt-merge-msg -F .git/FETCH_HEAD >actual && + test_cmp expected actual +' + +test_expect_success 'merge-msg -F in subdirectory' ' + + git config --unset-all merge.log + git config --unset-all merge.summary + git config merge.summary yes && + + git checkout master && + setdate && + git fetch . left right && + mkdir sub && + cp .git/FETCH_HEAD sub/FETCH_HEAD && + ( + cd sub && + git fmt-merge-msg -F FETCH_HEAD >../actual + ) && + test_cmp expected actual +' + test_done diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh index b81593780a..f275af8240 100755 --- a/t/t7002-grep.sh +++ b/t/t7002-grep.sh @@ -16,12 +16,13 @@ test_expect_success setup ' echo foo mmap bar_mmap echo foo_mmap bar mmap baz } >file && + echo ww w >w && echo x x xx x >x && echo y yy >y && echo zzz > z && mkdir t && echo test >t/t && - git add file x y z t/t && + git add file w x y z t/t && test_tick && git commit -m initial ' @@ -48,6 +49,12 @@ do diff expected actual ' + test_expect_success "grep -w $L (w)" ' + : >expected && + ! git grep -n -w -e "^w" >actual && + test_cmp expected actual + ' + test_expect_success "grep -w $L (x)" ' { echo ${HC}x:1:x x xx x diff --git a/t/t7406-submodule-reference.sh b/t/t7406-submodule-reference.sh new file mode 100755 index 0000000000..cc16d3f05d --- /dev/null +++ b/t/t7406-submodule-reference.sh @@ -0,0 +1,81 @@ +#!/bin/sh +# +# Copyright (c) 2009, Red Hat Inc, Author: Michael S. Tsirkin (mst@redhat.com) +# + +test_description='test clone --reference' +. ./test-lib.sh + +base_dir=`pwd` + +U=$base_dir/UPLOAD_LOG + +test_expect_success 'preparing first repository' \ +'test_create_repo A && cd A && +echo first > file1 && +git add file1 && +git commit -m A-initial' + +cd "$base_dir" + +test_expect_success 'preparing second repository' \ +'git clone A B && cd B && +echo second > file2 && +git add file2 && +git commit -m B-addition && +git repack -a -d && +git prune' + +cd "$base_dir" + +test_expect_success 'preparing supermodule' \ +'test_create_repo super && cd super && +echo file > file && +git add file && +git commit -m B-super-initial' + +cd "$base_dir" + +test_expect_success 'submodule add --reference' \ +'cd super && git submodule add --reference ../B "file://$base_dir/A" sub && +git commit -m B-super-added' + +cd "$base_dir" + +test_expect_success 'after add: existence of info/alternates' \ +'test `wc -l <super/sub/.git/objects/info/alternates` = 1' + +cd "$base_dir" + +test_expect_success 'that reference gets used with add' \ +'cd super/sub && +echo "0 objects, 0 kilobytes" > expected && +git count-objects > current && +diff expected current' + +cd "$base_dir" + +test_expect_success 'cloning supermodule' \ +'git clone super super-clone' + +cd "$base_dir" + +test_expect_success 'update with reference' \ +'cd super-clone && git submodule update --init --reference ../B' + +cd "$base_dir" + +test_expect_success 'after update: existence of info/alternates' \ +'test `wc -l <super-clone/sub/.git/objects/info/alternates` = 1' + +cd "$base_dir" + +test_expect_success 'that reference gets used with update' \ +'cd super-clone/sub && +echo "0 objects, 0 kilobytes" > expected && +git count-objects > current && +diff expected current' + +cd "$base_dir" + +test_done diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh new file mode 100755 index 0000000000..0773fe405b --- /dev/null +++ b/t/t7406-submodule-update.sh @@ -0,0 +1,140 @@ +#!/bin/sh +# +# Copyright (c) 2009 Red Hat, Inc. +# + +test_description='Test updating submodules + +This test verifies that "git submodule update" detaches the HEAD of the +submodule and "git submodule update --rebase" does not detach the HEAD. +' + +. ./test-lib.sh + + +compare_head() +{ + sha_master=`git-rev-list --max-count=1 master` + sha_head=`git-rev-list --max-count=1 HEAD` + + test "$sha_master" = "$sha_head" +} + + +test_expect_success 'setup a submodule tree' ' + echo file > file && + git add file && + test_tick && + git commit -m upstream + git clone . super && + git clone super submodule && + (cd super && + git submodule add ../submodule submodule && + test_tick && + git commit -m "submodule" && + git submodule init submodule + ) && + (cd submodule && + echo "line2" > file && + git add file && + git commit -m "Commit 2" + ) && + (cd super && + (cd submodule && + git pull --rebase origin + ) && + git add submodule && + git commit -m "submodule update" + ) +' + +test_expect_success 'submodule update detaching the HEAD ' ' + (cd super/submodule && + git reset --hard HEAD~1 + ) && + (cd super && + (cd submodule && + compare_head + ) && + git submodule update submodule && + cd submodule && + ! compare_head + ) +' + +test_expect_success 'submodule update --rebase staying on master' ' + (cd super/submodule && + git checkout master + ) && + (cd super && + (cd submodule && + compare_head + ) && + git submodule update --rebase submodule && + cd submodule && + compare_head + ) +' + +test_expect_success 'submodule update - rebase in .git/config' ' + (cd super && + git config submodule.submodule.update rebase + ) && + (cd super/submodule && + git reset --hard HEAD~1 + ) && + (cd super && + (cd submodule && + compare_head + ) && + git submodule update submodule && + cd submodule && + compare_head + ) +' + +test_expect_success 'submodule update - checkout in .git/config but --rebase given' ' + (cd super && + git config submodule.submodule.update checkout + ) && + (cd super/submodule && + git reset --hard HEAD~1 + ) && + (cd super && + (cd submodule && + compare_head + ) && + git submodule update --rebase submodule && + cd submodule && + compare_head + ) +' + +test_expect_success 'submodule update - checkout in .git/config' ' + (cd super && + git config submodule.submodule.update checkout + ) && + (cd super/submodule && + git reset --hard HEAD^ + ) && + (cd super && + (cd submodule && + compare_head + ) && + git submodule update submodule && + cd submodule && + ! compare_head + ) +' + +test_expect_success 'submodule init picks up rebase' ' + (cd super && + git config submodule.rebasing.url git://non-existing/git && + git config submodule.rebasing.path does-not-matter && + git config submodule.rebasing.update rebase && + git submodule init rebasing && + test "rebase" = $(git config submodule.rebasing.update) + ) +' + +test_done diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index 5998baf27b..8eec0fa9bc 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -183,4 +183,14 @@ test_expect_success 'commit message from stdin' ' commit_msg_is "Log with foo word" ' +test_expect_success 'commit -F overrides -t' ' + ( + cd subdir && + echo "-F log" > f.log && + echo "-t template" > t.template && + git commit --allow-empty -F f.log -t t.template + ) && + commit_msg_is "-F log" +' + test_done diff --git a/t/t8003-blame.sh b/t/t8003-blame.sh index 966bb0a61a..13c25f1d52 100755 --- a/t/t8003-blame.sh +++ b/t/t8003-blame.sh @@ -129,4 +129,19 @@ test_expect_success 'blame wholesale copy and more' ' ' +test_expect_success 'blame path that used to be a directory' ' + mkdir path && + echo A A A A A >path/file && + echo B B B B B >path/elif && + git add path && + test_tick && + git commit -m "path was a directory" && + rm -fr path && + echo A A A A A >path && + git add path && + test_tick && + git commit -m "path is a regular file" && + git blame HEAD^.. -- path +' + test_done diff --git a/t/t8005-blame-i18n.sh b/t/t8005-blame-i18n.sh index fcd5c26675..9cca14d080 100755 --- a/t/t8005-blame-i18n.sh +++ b/t/t8005-blame-i18n.sh @@ -4,7 +4,7 @@ test_description='git blame encoding conversion' . ./test-lib.sh . "$TEST_DIRECTORY"/t8005/utf8.txt -. "$TEST_DIRECTORY"/t8005/cp1251.txt +. "$TEST_DIRECTORY"/t8005/iso8859-5.txt . "$TEST_DIRECTORY"/t8005/sjis.txt test_expect_success 'setup the repository' ' @@ -13,14 +13,14 @@ test_expect_success 'setup the repository' ' git add file && git commit --author "$UTF8_NAME <utf8@localhost>" -m "$UTF8_MSG" && - echo "CP1251 LINE" >> file && + echo "ISO-8859-5 LINE" >> file && git add file && - git config i18n.commitencoding cp1251 && - git commit --author "$CP1251_NAME <cp1251@localhost>" -m "$CP1251_MSG" && + git config i18n.commitencoding ISO8859-5 && + git commit --author "$ISO8859_5_NAME <iso8859-5@localhost>" -m "$ISO8859_5_MSG" && echo "SJIS LINE" >> file && git add file && - git config i18n.commitencoding shift-jis && + git config i18n.commitencoding SJIS && git commit --author "$SJIS_NAME <sjis@localhost>" -m "$SJIS_MSG" ' @@ -41,17 +41,17 @@ test_expect_success \ ' cat >expected <<EOF -author $CP1251_NAME -summary $CP1251_MSG -author $CP1251_NAME -summary $CP1251_MSG -author $CP1251_NAME -summary $CP1251_MSG +author $ISO8859_5_NAME +summary $ISO8859_5_MSG +author $ISO8859_5_NAME +summary $ISO8859_5_MSG +author $ISO8859_5_NAME +summary $ISO8859_5_MSG EOF test_expect_success \ 'blame respects i18n.logoutputencoding' ' - git config i18n.logoutputencoding cp1251 && + git config i18n.logoutputencoding ISO8859-5 && git blame --incremental file | \ egrep "^(author|summary) " > actual && test_cmp actual expected @@ -67,8 +67,8 @@ summary $UTF8_MSG EOF test_expect_success \ - 'blame respects --encoding=utf-8' ' - git blame --incremental --encoding=utf-8 file | \ + 'blame respects --encoding=UTF-8' ' + git blame --incremental --encoding=UTF-8 file | \ egrep "^(author|summary) " > actual && test_cmp actual expected ' @@ -76,8 +76,8 @@ test_expect_success \ cat >expected <<EOF author $SJIS_NAME summary $SJIS_MSG -author $CP1251_NAME -summary $CP1251_MSG +author $ISO8859_5_NAME +summary $ISO8859_5_MSG author $UTF8_NAME summary $UTF8_MSG EOF diff --git a/t/t8005/cp1251.txt b/t/t8005/cp1251.txt deleted file mode 100644 index ce41e98b81..0000000000 --- a/t/t8005/cp1251.txt +++ /dev/null @@ -1,2 +0,0 @@ -CP1251_NAME="Èâàí Ïåòðîâè÷ Ñèäîðîâ" -CP1251_MSG="Òåñòîâîå ñîîáùåíèå" diff --git a/t/t8005/iso8859-5.txt b/t/t8005/iso8859-5.txt new file mode 100644 index 0000000000..2e4b80c8df --- /dev/null +++ b/t/t8005/iso8859-5.txt @@ -0,0 +1,2 @@ +ISO8859_5_NAME="¸ÒÐÝ ¿ÕâàÞÒØç ÁØÔÞàÞÒ" +ISO8859_5_MSG="ÂÕáâÞÒÞÕ áÞÞÑéÕÝØÕ" diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index ce26ea4ac5..2ce24cd5a6 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -533,7 +533,7 @@ test_expect_success 'utf8 Cc is rfc2047 encoded' ' --smtp-server="$(pwd)/fake.sendmail" \ outdir/*.patch && grep "^Cc:" msgtxt1 | - grep "=?utf-8?q?=C3=A0=C3=A9=C3=AC=C3=B6=C3=BA?= <utf8@example.com>" + grep "=?UTF-8?q?=C3=A0=C3=A9=C3=AC=C3=B6=C3=BA?= <utf8@example.com>" ' test_expect_success '--compose adds MIME for utf8 body' ' @@ -550,7 +550,7 @@ test_expect_success '--compose adds MIME for utf8 body' ' --smtp-server="$(pwd)/fake.sendmail" \ $patches && grep "^utf8 body" msgtxt1 && - grep "^Content-Type: text/plain; charset=utf-8" msgtxt1 + grep "^Content-Type: text/plain; charset=UTF-8" msgtxt1 ' test_expect_success '--compose respects user mime type' ' @@ -573,7 +573,7 @@ test_expect_success '--compose respects user mime type' ' $patches && grep "^utf8 body" msgtxt1 && grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 && - ! grep "^Content-Type: text/plain; charset=utf-8" msgtxt1 + ! grep "^Content-Type: text/plain; charset=UTF-8" msgtxt1 ' test_expect_success '--compose adds MIME for utf8 subject' ' @@ -586,7 +586,7 @@ test_expect_success '--compose adds MIME for utf8 subject' ' --smtp-server="$(pwd)/fake.sendmail" \ $patches && grep "^fake edit" msgtxt1 && - grep "^Subject: =?utf-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1 + grep "^Subject: =?UTF-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1 ' test_expect_success 'detects ambiguous reference/file conflict' ' diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh index 4eee2e9fa6..64aa7e2f10 100755 --- a/t/t9100-git-svn-basic.sh +++ b/t/t9100-git-svn-basic.sh @@ -31,7 +31,7 @@ test_expect_success \ echo "zzz" > bar/zzz && echo "#!/bin/sh" > exec.sh && chmod +x exec.sh && - svn import -m "import for git svn" . "$svnrepo" >/dev/null && + svn_cmd import -m "import for git svn" . "$svnrepo" >/dev/null && cd .. && rm -rf import && git svn init "$svnrepo"' @@ -51,7 +51,7 @@ test_expect_success "$name" ' git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ ${remotes_git_svn}..mybranch && - svn up "$SVN_TREE" && + svn_cmd up "$SVN_TREE" && test -d "$SVN_TREE"/dir && test ! -d "$SVN_TREE"/dir/a' @@ -118,7 +118,7 @@ test_expect_success "$name" ' git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ ${remotes_git_svn}..mybranch5 && - svn up "$SVN_TREE" && + svn_cmd up "$SVN_TREE" && test ! -x "$SVN_TREE"/exec.sh' @@ -129,7 +129,7 @@ test_expect_success "$name" ' git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ ${remotes_git_svn}..mybranch5 && - svn up "$SVN_TREE" && + svn_cmd up "$SVN_TREE" && test -x "$SVN_TREE"/exec.sh' @@ -141,7 +141,7 @@ test_expect_success "$name" ' git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ ${remotes_git_svn}..mybranch5 && - svn up "$SVN_TREE" && + svn_cmd up "$SVN_TREE" && test -L "$SVN_TREE"/exec.sh' name='new symlink is added to a file that was also just made executable' @@ -153,7 +153,7 @@ test_expect_success "$name" ' git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ ${remotes_git_svn}..mybranch5 && - svn up "$SVN_TREE" && + svn_cmd up "$SVN_TREE" && test -x "$SVN_TREE"/bar/zzz && test -L "$SVN_TREE"/exec-2.sh' @@ -166,7 +166,7 @@ test_expect_success "$name" ' git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ ${remotes_git_svn}..mybranch5 && - svn up "$SVN_TREE" && + svn_cmd up "$SVN_TREE" && test -f "$SVN_TREE"/exec-2.sh && test ! -L "$SVN_TREE"/exec-2.sh && test_cmp help "$SVN_TREE"/exec-2.sh' diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh index 1e31d6ea72..9da4178c94 100755 --- a/t/t9101-git-svn-props.sh +++ b/t/t9101-git-svn-props.sh @@ -48,7 +48,7 @@ EOF printf "\r\n" > empty_crlf a_empty_crlf=`git hash-object -w empty_crlf` - svn import --no-auto-props -m 'import for git svn' . "$svnrepo" >/dev/null + svn_cmd import --no-auto-props -m 'import for git svn' . "$svnrepo" >/dev/null cd .. rm -rf import @@ -57,13 +57,13 @@ test_expect_success 'setup some commits to svn' \ 'cd test_wc && echo Greetings >> kw.c && poke kw.c && - svn commit -m "Not yet an Id" && + svn_cmd commit -m "Not yet an Id" && echo Hello world >> kw.c && poke kw.c && - svn commit -m "Modified file, but still not yet an Id" && - svn propset svn:keywords Id kw.c && + svn_cmd commit -m "Modified file, but still not yet an Id" && + svn_cmd propset svn:keywords Id kw.c && poke kw.c && - svn commit -m "Propset Id" && + svn_cmd commit -m "Propset Id" && cd ..' test_expect_success 'initialize git svn' 'git svn init "$svnrepo"' @@ -83,16 +83,16 @@ test_expect_success 'raw $Id$ found in kw.c' "test '$expect' = '$got'" test_expect_success "propset CR on crlf files" \ 'cd test_wc && - svn propset svn:eol-style CR empty && - svn propset svn:eol-style CR crlf && - svn propset svn:eol-style CR ne_crlf && - svn commit -m "propset CR on crlf files" && + svn_cmd propset svn:eol-style CR empty && + svn_cmd propset svn:eol-style CR crlf && + svn_cmd propset svn:eol-style CR ne_crlf && + svn_cmd commit -m "propset CR on crlf files" && cd ..' test_expect_success 'fetch and pull latest from svn and checkout a new wc' \ 'git svn fetch && git pull . ${remotes_git_svn} && - svn co "$svnrepo" new_wc' + svn_cmd co "$svnrepo" new_wc' for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf do @@ -106,11 +106,11 @@ cd test_wc a_cr=`printf '$Id$\r\nHello\r\nWorld\r\n' | git hash-object --stdin` a_ne_cr=`printf '$Id$\r\nHello\r\nWorld' | git hash-object --stdin` test_expect_success 'Set CRLF on cr files' \ - 'svn propset svn:eol-style CRLF cr && - svn propset svn:eol-style CRLF ne_cr && - svn propset svn:keywords Id cr && - svn propset svn:keywords Id ne_cr && - svn commit -m "propset CRLF on cr files"' + 'svn_cmd propset svn:eol-style CRLF cr && + svn_cmd propset svn:eol-style CRLF ne_cr && + svn_cmd propset svn:keywords Id cr && + svn_cmd propset svn:keywords Id ne_cr && + svn_cmd commit -m "propset CRLF on cr files"' cd .. test_expect_success 'fetch and pull latest from svn' \ 'git svn fetch && git pull . ${remotes_git_svn}' @@ -140,10 +140,10 @@ test_expect_success 'test show-ignore' " cd test_wc && mkdir -p deeply/nested/directory && touch deeply/nested/directory/.keep && - svn add deeply && - svn up && - svn propset -R svn:ignore 'no-such-file*' . - svn commit -m 'propset svn:ignore' + svn_cmd add deeply && + svn_cmd up && + svn_cmd propset -R svn:ignore 'no-such-file*' . + svn_cmd commit -m 'propset svn:ignore' cd .. && git svn show-ignore > show-ignore.got && cmp show-ignore.expect show-ignore.got diff --git a/t/t9102-git-svn-deep-rmdir.sh b/t/t9102-git-svn-deep-rmdir.sh index e223218015..028fb19e09 100755 --- a/t/t9102-git-svn-deep-rmdir.sh +++ b/t/t9102-git-svn-deep-rmdir.sh @@ -9,7 +9,7 @@ test_expect_success 'initialize repo' ' mkdir -p deeply/nested/directory/number/2 && echo foo > deeply/nested/directory/number/1/file && echo foo > deeply/nested/directory/number/2/another && - svn import -m "import for git svn" . "$svnrepo" && + svn_cmd import -m "import for git svn" . "$svnrepo" && cd .. ' @@ -23,7 +23,7 @@ test_expect_success 'Try a commit on rmdir' ' git rm -f deeply/nested/directory/number/2/another && git commit -a -m "remove another" && git svn set-tree --rmdir HEAD && - svn ls -R "$svnrepo" | grep ^deeply/nested/directory/number/1 + svn_cmd ls -R "$svnrepo" | grep ^deeply/nested/directory/number/1 ' diff --git a/t/t9103-git-svn-tracked-directory-removed.sh b/t/t9103-git-svn-tracked-directory-removed.sh index 963dd95e4a..3413164cb1 100755 --- a/t/t9103-git-svn-tracked-directory-removed.sh +++ b/t/t9103-git-svn-tracked-directory-removed.sh @@ -10,15 +10,15 @@ test_expect_success 'make history for tracking' ' mkdir import && mkdir import/trunk && echo hello >> import/trunk/README && - svn import -m initial import "$svnrepo" && + svn_cmd import -m initial import "$svnrepo" && rm -rf import && - svn co "$svnrepo"/trunk trunk && + svn_cmd co "$svnrepo"/trunk trunk && echo bye bye >> trunk/README && - svn rm -m "gone" "$svnrepo"/trunk && + svn_cmd rm -m "gone" "$svnrepo"/trunk && rm -rf trunk && mkdir trunk && echo "new" > trunk/FOLLOWME && - svn import -m "new trunk" trunk "$svnrepo"/trunk + svn_cmd import -m "new trunk" trunk "$svnrepo"/trunk ' test_expect_success 'clone repo with git' ' diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh index ab9fa32220..78610b61e6 100755 --- a/t/t9104-git-svn-follow-parent.sh +++ b/t/t9104-git-svn-follow-parent.sh @@ -11,18 +11,18 @@ test_expect_success 'initialize repo' ' cd import && mkdir -p trunk && echo hello > trunk/readme && - svn import -m "initial" . "$svnrepo" && + svn_cmd import -m "initial" . "$svnrepo" && cd .. && - svn co "$svnrepo" wc && + svn_cmd co "$svnrepo" wc && cd wc && echo world >> trunk/readme && poke trunk/readme && - svn commit -m "another commit" && - svn up && - svn mv trunk thunk && + svn_cmd commit -m "another commit" && + svn_cmd up && + svn_cmd mv trunk thunk && echo goodbye >> thunk/readme && poke thunk/readme && - svn commit -m "bye now" && + svn_cmd commit -m "bye now" && cd .. ' @@ -51,7 +51,7 @@ test_expect_success 'init and fetch from one svn-remote' ' ' test_expect_success 'follow deleted parent' ' - (svn cp -m "resurrecting trunk as junk" \ + (svn_cmd cp -m "resurrecting trunk as junk" \ "$svnrepo"/trunk@2 "$svnrepo"/junk || svn cp -m "resurrecting trunk as junk" \ -r2 "$svnrepo"/trunk "$svnrepo"/junk) && @@ -97,8 +97,8 @@ test_expect_success 'follow higher-level parent' ' ' test_expect_success 'follow deleted directory' ' - svn mv -m "bye!" "$svnrepo"/glob/blob/hi "$svnrepo"/glob/blob/bye && - svn rm -m "remove glob" "$svnrepo"/glob && + svn_cmd mv -m "bye!" "$svnrepo"/glob/blob/hi "$svnrepo"/glob/blob/bye && + svn_cmd rm -m "remove glob" "$svnrepo"/glob && git svn init --minimize-url -i glob "$svnrepo"/glob && git svn fetch -i glob && test "`git cat-file blob refs/remotes/glob:blob/bye`" = hi && @@ -120,7 +120,7 @@ test_expect_success 'follow-parent avoids deleting relevant info' ' cd import && svn import -m "r9270 test" . "$svnrepo"/r9270 && cd .. && - svn co "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl r9270 && + svn_cmd co "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl r9270 && cd r9270 && svn mkdir native && svn mv t native/t && @@ -138,7 +138,7 @@ test_expect_success 'follow-parent avoids deleting relevant info' ' ' test_expect_success "track initial change if it was only made to parent" ' - svn cp -m "wheee!" "$svnrepo"/r9270/trunk "$svnrepo"/r9270/drunk && + svn_cmd cp -m "wheee!" "$svnrepo"/r9270/trunk "$svnrepo"/r9270/drunk && git svn init --minimize-url -i r9270-d \ "$svnrepo"/r9270/drunk/subversion/bindings/swig/perl/native/t && git svn fetch -i r9270-d && @@ -152,20 +152,20 @@ test_expect_success "track initial change if it was only made to parent" ' test_expect_success "follow-parent is atomic" ' ( cd wc && - svn up && - svn mkdir stunk && + svn_cmd up && + svn_cmd mkdir stunk && echo "trunk stunk" > stunk/readme && - svn add stunk/readme && - svn ci -m "trunk stunk" && + svn_cmd add stunk/readme && + svn_cmd ci -m "trunk stunk" && echo "stunk like junk" >> stunk/readme && - svn ci -m "really stunk" && + svn_cmd ci -m "really stunk" && echo "stink stank stunk" >> stunk/readme && - svn ci -m "even the grinch agrees" + svn_cmd ci -m "even the grinch agrees" ) && - svn copy -m "stunk flunked" "$svnrepo"/stunk "$svnrepo"/flunk && + svn_cmd copy -m "stunk flunked" "$svnrepo"/stunk "$svnrepo"/flunk && { svn cp -m "early stunk flunked too" \ "$svnrepo"/stunk@17 "$svnrepo"/flunked || - svn cp -m "early stunk flunked too" \ + svn_cmd cp -m "early stunk flunked too" \ -r17 "$svnrepo"/stunk "$svnrepo"/flunked; } && git svn init --minimize-url -i stunk "$svnrepo"/stunk && git svn fetch -i stunk && @@ -192,7 +192,7 @@ test_expect_success "follow-parent is atomic" ' ' test_expect_success "track multi-parent paths" ' - svn cp -m "resurrect /glob" "$svnrepo"/r9270 "$svnrepo"/glob && + svn_cmd cp -m "resurrect /glob" "$svnrepo"/r9270 "$svnrepo"/glob && git svn multi-fetch && test `git cat-file commit refs/remotes/glob | \ grep "^parent " | wc -l` -eq 2 diff --git a/t/t9105-git-svn-commit-diff.sh b/t/t9105-git-svn-commit-diff.sh index ba99abb6d9..dd48e9cba8 100755 --- a/t/t9105-git-svn-commit-diff.sh +++ b/t/t9105-git-svn-commit-diff.sh @@ -8,7 +8,7 @@ test_expect_success 'initialize repo' ' mkdir import && cd import && echo hello > readme && - svn import -m "initial" . "$svnrepo" && + svn_cmd import -m "initial" . "$svnrepo" && cd .. && echo hello > readme && git update-index --add readme && @@ -27,16 +27,16 @@ prev=`git rev-parse --verify HEAD^1` test_expect_success 'test the commit-diff command' ' test -n "$prev" && test -n "$head" && git svn commit-diff -r1 "$prev" "$head" "$svnrepo" && - svn co "$svnrepo" wc && + svn_cmd co "$svnrepo" wc && cmp readme wc/readme ' test_expect_success 'commit-diff to a sub-directory (with git svn config)' ' - svn import -m "sub-directory" import "$svnrepo"/subdir && + svn_cmd import -m "sub-directory" import "$svnrepo"/subdir && git svn init --minimize-url "$svnrepo"/subdir && git svn fetch && git svn commit-diff -r3 "$prev" "$head" && - svn cat "$svnrepo"/subdir/readme > readme.2 && + svn_cmd cat "$svnrepo"/subdir/readme > readme.2 && cmp readme readme.2 ' diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh index 6eb0fd85c8..12f21b700e 100755 --- a/t/t9106-git-svn-commit-diff-clobber.sh +++ b/t/t9106-git-svn-commit-diff-clobber.sh @@ -8,18 +8,18 @@ test_expect_success 'initialize repo' ' mkdir import && cd import && echo initial > file && - svn import -m "initial" . "$svnrepo" && + svn_cmd import -m "initial" . "$svnrepo" && cd .. && echo initial > file && git update-index --add file && git commit -a -m "initial" ' test_expect_success 'commit change from svn side' ' - svn co "$svnrepo" t.svn && + svn_cmd co "$svnrepo" t.svn && cd t.svn && echo second line from svn >> file && poke file && - svn commit -m "second line from svn" && + svn_cmd commit -m "second line from svn" && cd .. && rm -rf t.svn ' @@ -43,11 +43,11 @@ test_expect_success 'dcommit fails to commit because of conflict' ' git svn init "$svnrepo" && git svn fetch && git reset --hard refs/${remotes_git_svn} && - svn co "$svnrepo" t.svn && + svn_cmd co "$svnrepo" t.svn && cd t.svn && echo fourth line from svn >> file && poke file && - svn commit -m "fourth line from svn" && + svn_cmd commit -m "fourth line from svn" && cd .. && rm -rf t.svn && echo "fourth line from git" >> file && @@ -67,11 +67,11 @@ test_expect_success 'dcommit does the svn equivalent of an index merge' " " test_expect_success 'commit another change from svn side' ' - svn co "$svnrepo" t.svn && + svn_cmd co "$svnrepo" t.svn && cd t.svn && echo third line from svn >> file && poke file && - svn commit -m "third line from svn" && + svn_cmd commit -m "third line from svn" && cd .. && rm -rf t.svn ' diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh index acad16a6f0..3a9e07768d 100755 --- a/t/t9107-git-svn-migrate.sh +++ b/t/t9107-git-svn-migrate.sh @@ -12,7 +12,7 @@ test_expect_success 'setup old-looking metadata' ' mkdir -p $i && \ echo hello >> $i/README || exit 1 done && \ - svn import -m test . "$svnrepo" + svn_cmd import -m test . "$svnrepo" cd .. && git svn init "$svnrepo" && git svn fetch && diff --git a/t/t9108-git-svn-glob.sh b/t/t9108-git-svn-glob.sh index d8582b1aa5..d732d31302 100755 --- a/t/t9108-git-svn-glob.sh +++ b/t/t9108-git-svn-glob.sh @@ -14,30 +14,30 @@ test_expect_success 'test refspec globbing' ' mkdir -p trunk/src/a trunk/src/b trunk/doc && echo "hello world" > trunk/src/a/readme && echo "goodbye world" > trunk/src/b/readme && - svn import -m "initial" trunk "$svnrepo"/trunk && - svn co "$svnrepo" tmp && + svn_cmd import -m "initial" trunk "$svnrepo"/trunk && + svn_cmd co "$svnrepo" tmp && ( cd tmp && mkdir branches tags && - svn add branches tags && - svn cp trunk branches/start && - svn commit -m "start a new branch" && - svn up && + svn_cmd add branches tags && + svn_cmd cp trunk branches/start && + svn_cmd commit -m "start a new branch" && + svn_cmd up && echo "hi" >> branches/start/src/b/readme && poke branches/start/src/b/readme && echo "hey" >> branches/start/src/a/readme && poke branches/start/src/a/readme && - svn commit -m "hi" && - svn up && - svn cp branches/start tags/end && + svn_cmd commit -m "hi" && + svn_cmd up && + svn_cmd cp branches/start tags/end && echo "bye" >> tags/end/src/b/readme && poke tags/end/src/b/readme && echo "aye" >> tags/end/src/a/readme && poke tags/end/src/a/readme && - svn commit -m "the end" && + svn_cmd commit -m "the end" && echo "byebye" >> tags/end/src/b/readme && poke tags/end/src/b/readme && - svn commit -m "nothing to see here" + svn_cmd commit -m "nothing to see here" ) && git config --add svn-remote.svn.url "$svnrepo" && git config --add svn-remote.svn.fetch \ @@ -72,7 +72,7 @@ test_expect_success 'test left-hand-side only globbing' ' cd tmp && echo "try try" >> tags/end/src/b/readme && poke tags/end/src/b/readme && - svn commit -m "try to try" + svn_cmd commit -m "try to try" ) && git svn fetch two && test `git rev-list refs/remotes/two/tags/end | wc -l` -eq 6 && @@ -102,7 +102,7 @@ test_expect_success 'test disallow multi-globs' ' cd tmp && echo "try try" >> tags/end/src/b/readme && poke tags/end/src/b/readme && - svn commit -m "try to try" + svn_cmd commit -m "try to try" ) && test_must_fail git svn fetch three 2> stderr.three && test_cmp expect.three stderr.three diff --git a/t/t9109-git-svn-multi-glob.sh b/t/t9109-git-svn-multi-glob.sh index 8f79c3f251..c318f9f946 100755 --- a/t/t9109-git-svn-multi-glob.sh +++ b/t/t9109-git-svn-multi-glob.sh @@ -14,30 +14,30 @@ test_expect_success 'test refspec globbing' ' mkdir -p trunk/src/a trunk/src/b trunk/doc && echo "hello world" > trunk/src/a/readme && echo "goodbye world" > trunk/src/b/readme && - svn import -m "initial" trunk "$svnrepo"/trunk && - svn co "$svnrepo" tmp && + svn_cmd import -m "initial" trunk "$svnrepo"/trunk && + svn_cmd co "$svnrepo" tmp && ( cd tmp && mkdir branches branches/v1 tags && - svn add branches tags && - svn cp trunk branches/v1/start && - svn commit -m "start a new branch" && - svn up && + svn_cmd add branches tags && + svn_cmd cp trunk branches/v1/start && + svn_cmd commit -m "start a new branch" && + svn_cmd up && echo "hi" >> branches/v1/start/src/b/readme && poke branches/v1/start/src/b/readme && echo "hey" >> branches/v1/start/src/a/readme && poke branches/v1/start/src/a/readme && - svn commit -m "hi" && - svn up && - svn cp branches/v1/start tags/end && + svn_cmd commit -m "hi" && + svn_cmd up && + svn_cmd cp branches/v1/start tags/end && echo "bye" >> tags/end/src/b/readme && poke tags/end/src/b/readme && echo "aye" >> tags/end/src/a/readme && poke tags/end/src/a/readme && - svn commit -m "the end" && + svn_cmd commit -m "the end" && echo "byebye" >> tags/end/src/b/readme && poke tags/end/src/b/readme && - svn commit -m "nothing to see here" + svn_cmd commit -m "nothing to see here" ) && git config --add svn-remote.svn.url "$svnrepo" && git config --add svn-remote.svn.fetch \ @@ -72,7 +72,7 @@ test_expect_success 'test left-hand-side only globbing' ' cd tmp && echo "try try" >> tags/end/src/b/readme && poke tags/end/src/b/readme && - svn commit -m "try to try" + svn_cmd commit -m "try to try" ) && git svn fetch two && test `git rev-list refs/remotes/two/tags/end | wc -l` -eq 6 && @@ -97,25 +97,25 @@ test_expect_success 'test another branch' ' ( cd tmp && mkdir branches/v2 && - svn add branches/v2 && - svn cp trunk branches/v2/start && - svn commit -m "Another versioned branch" && - svn up && + svn_cmd add branches/v2 && + svn_cmd cp trunk branches/v2/start && + svn_cmd commit -m "Another versioned branch" && + svn_cmd up && echo "hello" >> branches/v2/start/src/b/readme && poke branches/v2/start/src/b/readme && echo "howdy" >> branches/v2/start/src/a/readme && poke branches/v2/start/src/a/readme && - svn commit -m "Changed 2 in v2/start" && - svn up && - svn cp branches/v2/start tags/next && + svn_cmd commit -m "Changed 2 in v2/start" && + svn_cmd up && + svn_cmd cp branches/v2/start tags/next && echo "bye" >> tags/next/src/b/readme && poke tags/next/src/b/readme && echo "aye" >> tags/next/src/a/readme && poke tags/next/src/a/readme && - svn commit -m "adding more" && + svn_cmd commit -m "adding more" && echo "byebye" >> tags/next/src/b/readme && poke tags/next/src/b/readme && - svn commit -m "adios" + svn_cmd commit -m "adios" ) && git config --add svn-remote.four.url "$svnrepo" && git config --add svn-remote.four.fetch trunk:refs/remotes/four/trunk && @@ -151,7 +151,7 @@ test_expect_success 'test disallow multiple globs' ' cd tmp && echo "try try" >> tags/end/src/b/readme && poke tags/end/src/b/readme && - svn commit -m "try to try" + svn_cmd commit -m "try to try" ) && test_must_fail git svn fetch three 2> stderr.three && test_cmp expect.three stderr.three diff --git a/t/t9113-git-svn-dcommit-new-file.sh b/t/t9113-git-svn-dcommit-new-file.sh index e9b6128b3f..e8479cec7a 100755 --- a/t/t9113-git-svn-dcommit-new-file.sh +++ b/t/t9113-git-svn-dcommit-new-file.sh @@ -15,7 +15,7 @@ test_description='git svn dcommit new files over svn:// test' require_svnserve test_expect_success 'start tracking an empty repo' ' - svn mkdir -m "empty dir" "$svnrepo"/empty-dir && + svn_cmd mkdir -m "empty dir" "$svnrepo"/empty-dir && echo "[general]" > "$rawsvnrepo"/conf/svnserve.conf && echo anon-access = write >> "$rawsvnrepo"/conf/svnserve.conf && start_svnserve && diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh index 17b2855c4f..84f7c9b4bb 100755 --- a/t/t9114-git-svn-dcommit-merge.sh +++ b/t/t9114-git-svn-dcommit-merge.sh @@ -35,12 +35,12 @@ EOF } test_expect_success 'setup svn repository' ' - svn co "$svnrepo" mysvnwork && + svn_cmd co "$svnrepo" mysvnwork && mkdir -p mysvnwork/trunk && cd mysvnwork && big_text_block >> trunk/README && - svn add trunk && - svn ci -m "first commit" trunk && + svn_cmd add trunk && + svn_cmd ci -m "first commit" trunk && cd .. ' diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh index fd6d1d2046..0374a7476b 100755 --- a/t/t9116-git-svn-log.sh +++ b/t/t9116-git-svn-log.sh @@ -14,7 +14,7 @@ test_expect_success 'setup repository and import' ' mkdir -p $i && \ echo hello >> $i/README || exit 1 done && \ - svn import -m test . "$svnrepo" + svn_cmd import -m test . "$svnrepo" cd .. && git svn init "$svnrepo" -T trunk -b branches -t tags && git svn fetch && diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh index dde46cd92f..b7ef9e2589 100755 --- a/t/t9117-git-svn-init-clone.sh +++ b/t/t9117-git-svn-init-clone.sh @@ -16,7 +16,7 @@ cd tmp test_expect_success 'setup svnrepo' ' mkdir project project/trunk project/branches project/tags && echo foo > project/trunk/foo && - svn import -m "$test_description" project "$svnrepo"/project && + svn_cmd import -m "$test_description" project "$svnrepo"/project && rm -rf project ' diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh index 7a7c128687..ac52bff0ef 100755 --- a/t/t9118-git-svn-funky-branch-names.sh +++ b/t/t9118-git-svn-funky-branch-names.sh @@ -13,13 +13,13 @@ scary_ref='Abo-Uebernahme%20(Bug%20#994)' test_expect_success 'setup svnrepo' ' mkdir project project/trunk project/branches project/tags && echo foo > project/trunk/foo && - svn import -m "$test_description" project "$svnrepo/pr ject" && + svn_cmd import -m "$test_description" project "$svnrepo/pr ject" && rm -rf project && - svn cp -m "fun" "$svnrepo/pr ject/trunk" \ + svn_cmd cp -m "fun" "$svnrepo/pr ject/trunk" \ "$svnrepo/pr ject/branches/fun plugin" && - svn cp -m "more fun!" "$svnrepo/pr ject/branches/fun plugin" \ + svn_cmd cp -m "more fun!" "$svnrepo/pr ject/branches/fun plugin" \ "$svnrepo/pr ject/branches/more fun plugin!" && - svn cp -m "scary" "$svnrepo/pr ject/branches/fun plugin" \ + svn_cmd cp -m "scary" "$svnrepo/pr ject/branches/fun plugin" \ "$svnrepo/pr ject/branches/$scary_uri" && start_httpd ' diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh index 27dd7c273a..95741cbbac 100755 --- a/t/t9119-git-svn-info.sh +++ b/t/t9119-git-svn-info.sh @@ -7,7 +7,7 @@ test_description='git svn info' . ./lib-git-svn.sh # Tested with: svn, version 1.4.4 (r25188) -v=`svn --version | sed -n -e 's/^svn, version \(1\.[0-9]*\.[0-9]*\).*$/\1/p'` +v=`svn_cmd --version | sed -n -e 's/^svn, version \(1\.[0-9]*\.[0-9]*\).*$/\1/p'` case $v in 1.[45].*) ;; @@ -31,7 +31,7 @@ ptouch() { my $atime = $mtime; utime $atime, $mtime, $git_file; } - ' "`svn info $2 | grep '^Text Last Updated:'`" "$1" + ' "`svn_cmd info $2 | grep '^Text Last Updated:'`" "$1" } quoted_svnrepo="$(echo $svnrepo | sed 's/ /%20/')" @@ -45,14 +45,14 @@ test_expect_success 'setup repository and import' ' mkdir directory && touch directory/.placeholder && ln -s directory symlink-directory && - svn import -m "initial" . "$svnrepo" && + svn_cmd import -m "initial" . "$svnrepo" && cd .. && - svn co "$svnrepo" svnwc && + svn_cmd co "$svnrepo" svnwc && cd svnwc && echo foo > foo && - svn add foo && - svn commit -m "change outside directory" && - svn update && + svn_cmd add foo && + svn_cmd commit -m "change outside directory" && + svn_cmd update && cd .. && mkdir gitwc && cd gitwc && @@ -143,7 +143,7 @@ test_expect_success 'info added-file' " cp gitwc/added-file svnwc/added-file && ptouch gitwc/added-file svnwc/added-file && cd svnwc && - svn add added-file > /dev/null && + svn_cmd add added-file > /dev/null && cd .. && (cd svnwc; svn info added-file) > expected.info-added-file && (cd gitwc; git svn info added-file) > actual.info-added-file && @@ -160,7 +160,7 @@ test_expect_success 'info added-directory' " ptouch gitwc/added-directory svnwc/added-directory && touch gitwc/added-directory/.placeholder && cd svnwc && - svn add added-directory > /dev/null && + svn_cmd add added-directory > /dev/null && cd .. && cd gitwc && git add added-directory && @@ -184,7 +184,7 @@ test_expect_success 'info added-symlink-file' " cd .. && cd svnwc && ln -s added-file added-symlink-file && - svn add added-symlink-file > /dev/null && + svn_cmd add added-symlink-file > /dev/null && cd .. && ptouch gitwc/added-symlink-file svnwc/added-symlink-file && (cd svnwc; svn info added-symlink-file) \ @@ -207,7 +207,7 @@ test_expect_success 'info added-symlink-directory' " cd .. && cd svnwc && ln -s added-directory added-symlink-directory && - svn add added-symlink-directory > /dev/null && + svn_cmd add added-symlink-directory > /dev/null && cd .. && ptouch gitwc/added-symlink-directory svnwc/added-symlink-directory && (cd svnwc; svn info added-symlink-directory) \ @@ -233,7 +233,7 @@ test_expect_success 'info deleted-file' " git rm -f file > /dev/null && cd .. && cd svnwc && - svn rm --force file > /dev/null && + svn_cmd rm --force file > /dev/null && cd .. && (cd svnwc; svn info file) | sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ @@ -254,7 +254,7 @@ test_expect_success 'info deleted-directory' " git rm -r -f directory > /dev/null && cd .. && cd svnwc && - svn rm --force directory > /dev/null && + svn_cmd rm --force directory > /dev/null && cd .. && (cd svnwc; svn info directory) | sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ @@ -275,7 +275,7 @@ test_expect_success 'info deleted-symlink-file' " git rm -f symlink-file > /dev/null && cd .. && cd svnwc && - svn rm --force symlink-file > /dev/null && + svn_cmd rm --force symlink-file > /dev/null && cd .. && (cd svnwc; svn info symlink-file) | sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ @@ -297,7 +297,7 @@ test_expect_success 'info deleted-symlink-directory' " git rm -f symlink-directory > /dev/null && cd .. && cd svnwc && - svn rm --force symlink-directory > /dev/null && + svn_cmd rm --force symlink-directory > /dev/null && cd .. && (cd svnwc; svn info symlink-directory) | sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \ diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh index ef2c0523cd..f159ab689b 100755 --- a/t/t9120-git-svn-clone-with-percent-escapes.sh +++ b/t/t9120-git-svn-clone-with-percent-escapes.sh @@ -9,22 +9,17 @@ test_description='git svn clone with percent escapes' test_expect_success 'setup svnrepo' ' mkdir project project/trunk project/branches project/tags && echo foo > project/trunk/foo && - svn import -m "$test_description" project "$svnrepo/pr ject" && + svn_cmd import -m "$test_description" project "$svnrepo/pr ject" && rm -rf project && start_httpd ' -if test "$SVN_HTTPD_PORT" = "" -then - test_expect_failure 'test clone with percent escapes - needs SVN_HTTPD_PORT set' 'false' -else - test_expect_success 'test clone with percent escapes' ' - git svn clone "$svnrepo/pr%20ject" clone && - cd clone && - git rev-parse refs/${remotes_git_svn} && - cd .. - ' -fi +test_expect_success 'test clone with percent escapes' ' + git svn clone "$svnrepo/pr%20ject" clone && + cd clone && + git rev-parse refs/${remotes_git_svn} && + cd .. +' stop_httpd diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh index 1b1cf47281..30013b7bb9 100755 --- a/t/t9122-git-svn-author.sh +++ b/t/t9122-git-svn-author.sh @@ -4,12 +4,12 @@ test_description='git svn authorship' . ./lib-git-svn.sh test_expect_success 'setup svn repository' ' - svn checkout "$svnrepo" work.svn && + svn_cmd checkout "$svnrepo" work.svn && ( cd work.svn && echo >file - svn add file - svn commit -m "first commit" file + svn_cmd add file + svn_cmd commit -m "first commit" file ) ' @@ -74,10 +74,10 @@ test_expect_success 'interact with it via git svn' ' # Make sure there are no svn commit messages with excess blank lines ( cd work.svn && - svn up && + svn_cmd up && - test $(svn log -r2:2 | wc -l) = 5 && - test $(svn log -r4:4 | wc -l) = 7 + test $(svn_cmd log -r2:2 | wc -l) = 5 && + test $(svn_cmd log -r4:4 | wc -l) = 7 ) ' diff --git a/t/t9123-git-svn-rebuild-with-rewriteroot.sh b/t/t9123-git-svn-rebuild-with-rewriteroot.sh index cf0415274c..045521615c 100755 --- a/t/t9123-git-svn-rebuild-with-rewriteroot.sh +++ b/t/t9123-git-svn-rebuild-with-rewriteroot.sh @@ -10,7 +10,7 @@ test_description='git svn respects rewriteRoot during rebuild' mkdir import cd import touch foo - svn import -m 'import for git svn' . "$svnrepo" >/dev/null + svn_cmd import -m 'import for git svn' . "$svnrepo" >/dev/null cd .. rm -rf import diff --git a/t/t9124-git-svn-dcommit-auto-props.sh b/t/t9124-git-svn-dcommit-auto-props.sh index 263dbf5fc2..d6b076f6b7 100755 --- a/t/t9124-git-svn-dcommit-auto-props.sh +++ b/t/t9124-git-svn-dcommit-auto-props.sh @@ -21,7 +21,7 @@ test_expect_success 'initialize git svn' ' ( cd import && echo foo >foo && - svn import -m "import for git svn" . "$svnrepo" + svn_cmd import -m "import for git svn" . "$svnrepo" ) && rm -rf import && git svn init "$svnrepo" @@ -61,23 +61,23 @@ test_expect_success 'check resulting svn repository' ' ( mkdir work && cd work && - svn co "$svnrepo" && + svn_cmd co "$svnrepo" && cd svnrepo && # Check properties from first commit. - test "x$(svn propget svn:executable exec1.sh)" = "x*" && - test "x$(svn propget svn:mime-type exec1.sh)" = \ + test "x$(svn_cmd propget svn:executable exec1.sh)" = "x*" && + test "x$(svn_cmd propget svn:mime-type exec1.sh)" = \ "xapplication/x-shellscript" && - test "x$(svn propget svn:mime-type hello.txt)" = "xtext/plain" && - test "x$(svn propget svn:eol-style hello.txt)" = "xnative" && - test "x$(svn propget svn:mime-type bar)" = "x" && + test "x$(svn_cmd propget svn:mime-type hello.txt)" = "xtext/plain" && + test "x$(svn_cmd propget svn:eol-style hello.txt)" = "xnative" && + test "x$(svn_cmd propget svn:mime-type bar)" = "x" && # Check properties from second commit. - test "x$(svn propget svn:executable exec2.sh)" = "x*" && - test "x$(svn propget svn:mime-type exec2.sh)" = "x" && - test "x$(svn propget svn:mime-type world.txt)" = "x" && - test "x$(svn propget svn:eol-style world.txt)" = "x" && - test "x$(svn propget svn:mime-type zot)" = "x" + test "x$(svn_cmd propget svn:executable exec2.sh)" = "x*" && + test "x$(svn_cmd propget svn:mime-type exec2.sh)" = "x" && + test "x$(svn_cmd propget svn:mime-type world.txt)" = "x" && + test "x$(svn_cmd propget svn:eol-style world.txt)" = "x" && + test "x$(svn_cmd propget svn:mime-type zot)" = "x" ) ' @@ -89,12 +89,12 @@ test_expect_success 'check renamed file' ' git svn dcommit --config-dir=user && ( cd work/svnrepo && - svn up && + svn_cmd up && test ! -e foo && test -e foo.sh && - test "x$(svn propget svn:mime-type foo.sh)" = \ + test "x$(svn_cmd propget svn:mime-type foo.sh)" = \ "xapplication/x-shellscript" && - test "x$(svn propget svn:eol-style foo.sh)" = "xLF" + test "x$(svn_cmd propget svn:eol-style foo.sh)" = "xLF" ) ' diff --git a/t/t9125-git-svn-multi-glob-branch-names.sh b/t/t9125-git-svn-multi-glob-branch-names.sh index 475c751c1c..c19418614f 100755 --- a/t/t9125-git-svn-multi-glob-branch-names.sh +++ b/t/t9125-git-svn-multi-glob-branch-names.sh @@ -8,11 +8,11 @@ test_expect_success 'setup svnrepo' ' mkdir project project/trunk project/branches \ project/branches/v14.1 project/tags && echo foo > project/trunk/foo && - svn import -m "$test_description" project "$svnrepo/project" && + svn_cmd import -m "$test_description" project "$svnrepo/project" && rm -rf project && - svn cp -m "fun" "$svnrepo/project/trunk" \ + svn_cmd cp -m "fun" "$svnrepo/project/trunk" \ "$svnrepo/project/branches/v14.1/beta" && - svn cp -m "more fun!" "$svnrepo/project/branches/v14.1/beta" \ + svn_cmd cp -m "more fun!" "$svnrepo/project/branches/v14.1/beta" \ "$svnrepo/project/branches/v14.1/gold" ' diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh index 87696a92dc..4aab8ecc14 100755 --- a/t/t9127-git-svn-partial-rebuild.sh +++ b/t/t9127-git-svn-partial-rebuild.sh @@ -14,21 +14,21 @@ test_expect_success 'initialize svnrepo' ' cd trunk && echo foo > foo && cd .. && - svn import -m "import for git-svn" . "$svnrepo" >/dev/null && - svn copy "$svnrepo"/trunk "$svnrepo"/branches/a \ + svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null && + svn_cmd copy "$svnrepo"/trunk "$svnrepo"/branches/a \ -m "created branch a" && cd .. && rm -rf import && - svn co "$svnrepo"/trunk trunk && + svn_cmd co "$svnrepo"/trunk trunk && cd trunk && echo bar >> foo && - svn ci -m "updated trunk" && + svn_cmd ci -m "updated trunk" && cd .. && - svn co "$svnrepo"/branches/a a && + svn_cmd co "$svnrepo"/branches/a a && cd a && echo baz >> a && - svn add a && - svn ci -m "updated a" && + svn_cmd add a && + svn_cmd ci -m "updated a" && cd .. && git svn init --stdlayout "$svnrepo" ) diff --git a/t/t9128-git-svn-cmd-branch.sh b/t/t9128-git-svn-cmd-branch.sh index 252daa7e1a..807e494a3a 100755 --- a/t/t9128-git-svn-cmd-branch.sh +++ b/t/t9128-git-svn-cmd-branch.sh @@ -14,13 +14,13 @@ test_expect_success 'initialize svnrepo' ' cd trunk && echo foo > foo && cd .. && - svn import -m "import for git-svn" . "$svnrepo" >/dev/null && + svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null && cd .. && rm -rf import && - svn co "$svnrepo"/trunk trunk && + svn_cmd co "$svnrepo"/trunk trunk && cd trunk && echo bar >> foo && - svn ci -m "updated trunk" && + svn_cmd ci -m "updated trunk" && cd .. && rm -rf trunk ) @@ -57,14 +57,14 @@ test_expect_success 'git svn branch tests' ' ' test_expect_success 'branch uses correct svn-remote' ' - (svn co "$svnrepo" svn && + (svn_cmd co "$svnrepo" svn && cd svn && mkdir mirror && - svn add mirror && - svn copy trunk mirror/ && - svn copy tags mirror/ && - svn copy branches mirror/ && - svn ci -m "made mirror" ) && + svn_cmd add mirror && + svn_cmd copy trunk mirror/ && + svn_cmd copy tags mirror/ && + svn_cmd copy branches mirror/ && + svn_cmd ci -m "made mirror" ) && rm -rf svn && git svn init -s -R mirror --prefix=mirror/ "$svnrepo"/mirror && git svn fetch -R mirror && diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh index 3200ab38ef..b9224bdb20 100755 --- a/t/t9129-git-svn-i18n-commitencoding.sh +++ b/t/t9129-git-svn-i18n-commitencoding.sh @@ -29,16 +29,16 @@ compare_svn_head_with () { test_cmp current "$1" } -for H in ISO-8859-1 EUCJP ISO-2022-JP +for H in ISO8859-1 eucJP ISO-2022-JP do test_expect_success "$H setup" ' mkdir $H && - svn import -m "$H test" $H "$svnrepo"/$H && + svn_cmd import -m "$H test" $H "$svnrepo"/$H && git svn clone "$svnrepo"/$H $H ' done -for H in ISO-8859-1 EUCJP ISO-2022-JP +for H in ISO8859-1 eucJP ISO-2022-JP do test_expect_success "$H commit on git side" ' ( @@ -55,7 +55,7 @@ do ' done -for H in ISO-8859-1 EUCJP ISO-2022-JP +for H in ISO8859-1 eucJP ISO-2022-JP do test_expect_success "$H dcommit to svn" ' ( @@ -77,12 +77,12 @@ fi test_expect_success UTF8 'ISO-8859-1 should match UTF-8 in svn' ' ( - cd ISO-8859-1 && + cd ISO8859-1 && compare_svn_head_with "$TEST_DIRECTORY"/t3900/1-UTF-8.txt ) ' -for H in EUCJP ISO-2022-JP +for H in eucJP ISO-2022-JP do test_expect_success UTF8 "$H should match UTF-8 in svn" ' ( diff --git a/t/t9130-git-svn-authors-file.sh b/t/t9130-git-svn-authors-file.sh index b8fb277562..f5abdb3c7f 100755 --- a/t/t9130-git-svn-authors-file.sh +++ b/t/t9130-git-svn-authors-file.sh @@ -15,7 +15,7 @@ EOF test_expect_success 'setup svnrepo' ' for i in aa bb cc dd do - svn mkdir -m $i --username $i "$svnrepo"/$i + svn_cmd mkdir -m $i --username $i "$svnrepo"/$i done ' @@ -52,13 +52,13 @@ test_expect_success 'continues to import once authors have been added' ' ' test_expect_success 'authors-file against globs' ' - svn mkdir -m globs --username aa \ + svn_cmd mkdir -m globs --username aa \ "$svnrepo"/aa/trunk "$svnrepo"/aa/branches "$svnrepo"/aa/tags && git svn clone --authors-file=svn-authors -s "$svnrepo"/aa aa-work && for i in bb ee cc do branch="aa/branches/$i" - svn mkdir -m "$branch" --username $i "$svnrepo/$branch" + svn_cmd mkdir -m "$branch" --username $i "$svnrepo/$branch" done ' diff --git a/t/t9133-git-svn-nested-git-repo.sh b/t/t9133-git-svn-nested-git-repo.sh index 893f57ef73..f3c30e63b7 100755 --- a/t/t9133-git-svn-nested-git-repo.sh +++ b/t/t9133-git-svn-nested-git-repo.sh @@ -7,19 +7,19 @@ test_description='git svn property tests' . ./lib-git-svn.sh test_expect_success 'setup repo with a git repo inside it' ' - svn co "$svnrepo" s && + svn_cmd co "$svnrepo" s && ( cd s && git init && test -f .git/HEAD && > .git/a && echo a > a && - svn add .git a && - svn commit -m "create a nested git repo" && - svn up && + svn_cmd add .git a && + svn_cmd commit -m "create a nested git repo" && + svn_cmd up && echo hi >> .git/a && - svn commit -m "modify .git/a" && - svn up + svn_cmd commit -m "modify .git/a" && + svn_cmd up ) ' @@ -33,9 +33,9 @@ test_expect_success 'SVN-side change outside of .git' ' ( cd s && echo b >> a && - svn commit -m "SVN-side change outside of .git" && - svn up && - svn log -v | fgrep "SVN-side change outside of .git" + svn_cmd commit -m "SVN-side change outside of .git" && + svn_cmd up && + svn_cmd log -v | fgrep "SVN-side change outside of .git" ) ' @@ -56,10 +56,10 @@ test_expect_success 'SVN-side change inside of .git' ' git add a && git commit -m "add a inside an SVN repo" && git log && - svn add --force .git && - svn commit -m "SVN-side change inside of .git" && - svn up && - svn log -v | fgrep "SVN-side change inside of .git" + svn_cmd add --force .git && + svn_cmd commit -m "SVN-side change inside of .git" && + svn_cmd up && + svn_cmd log -v | fgrep "SVN-side change inside of .git" ) ' @@ -80,9 +80,9 @@ test_expect_success 'SVN-side change in and out of .git' ' echo c >> a && git add a && git commit -m "add a inside an SVN repo" && - svn commit -m "SVN-side change in and out of .git" && - svn up && - svn log -v | fgrep "SVN-side change in and out of .git" + svn_cmd commit -m "SVN-side change in and out of .git" && + svn_cmd up && + svn_cmd log -v | fgrep "SVN-side change in and out of .git" ) ' diff --git a/t/t9134-git-svn-ignore-paths.sh b/t/t9134-git-svn-ignore-paths.sh index 71fdc4a69d..09ff10cd9b 100755 --- a/t/t9134-git-svn-ignore-paths.sh +++ b/t/t9134-git-svn-ignore-paths.sh @@ -8,19 +8,19 @@ test_description='git svn property tests' . ./lib-git-svn.sh test_expect_success 'setup test repository' ' - svn co "$svnrepo" s && + svn_cmd co "$svnrepo" s && ( cd s && mkdir qqq www && echo test_qqq > qqq/test_qqq.txt && echo test_www > www/test_www.txt && - svn add qqq && - svn add www && - svn commit -m "create some files" && - svn up && + svn_cmd add qqq && + svn_cmd add www && + svn_cmd commit -m "create some files" && + svn_cmd up && echo hi >> www/test_www.txt && - svn commit -m "modify www/test_www.txt" && - svn up + svn_cmd commit -m "modify www/test_www.txt" && + svn_cmd up ) ' @@ -51,9 +51,9 @@ test_expect_success 'SVN-side change outside of www' ' ( cd s && echo b >> qqq/test_qqq.txt && - svn commit -m "SVN-side change outside of www" && - svn up && - svn log -v | fgrep "SVN-side change outside of www" + svn_cmd commit -m "SVN-side change outside of www" && + svn_cmd up && + svn_cmd log -v | fgrep "SVN-side change outside of www" ) ' @@ -83,9 +83,9 @@ test_expect_success 'SVN-side change inside of ignored www' ' ( cd s && echo zaq >> www/test_www.txt - svn commit -m "SVN-side change inside of www/test_www.txt" && - svn up && - svn log -v | fgrep "SVN-side change inside of www/test_www.txt" + svn_cmd commit -m "SVN-side change inside of www/test_www.txt" && + svn_cmd up && + svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt" ) ' @@ -116,9 +116,9 @@ test_expect_success 'SVN-side change in and out of ignored www' ' cd s && echo cvf >> www/test_www.txt echo ygg >> qqq/test_qqq.txt - svn commit -m "SVN-side change in and out of ignored www" && - svn up && - svn log -v | fgrep "SVN-side change in and out of ignored www" + svn_cmd commit -m "SVN-side change in and out of ignored www" && + svn_cmd up && + svn_cmd log -v | fgrep "SVN-side change in and out of ignored www" ) ' diff --git a/t/t9137-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh index fd185011b7..636ca0abb9 100755 --- a/t/t9137-git-svn-dcommit-clobber-series.sh +++ b/t/t9137-git-svn-dcommit-clobber-series.sh @@ -8,7 +8,7 @@ test_expect_success 'initialize repo' ' mkdir import && cd import && awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file - svn import -m "initial" . "$svnrepo" && + svn_cmd import -m "initial" . "$svnrepo" && cd .. && git svn init "$svnrepo" && git svn fetch && @@ -18,14 +18,14 @@ test_expect_success 'initialize repo' ' test_expect_success '(supposedly) non-conflicting change from SVN' ' test x"`sed -n -e 58p < file`" = x58 && test x"`sed -n -e 61p < file`" = x61 && - svn co "$svnrepo" tmp && + svn_cmd co "$svnrepo" tmp && cd tmp && perl -i.bak -p -e "s/^58$/5588/" file && perl -i.bak -p -e "s/^61$/6611/" file && poke file && test x"`sed -n -e 58p < file`" = x5588 && test x"`sed -n -e 61p < file`" = x6611 && - svn commit -m "58 => 5588, 61 => 6611" && + svn_cmd commit -m "58 => 5588, 61 => 6611" && cd .. ' @@ -46,7 +46,7 @@ test_expect_success 'change file but in unrelated area' " test x\"\`sed -n -e 7p < file\`\" = x7777 && git commit -m '4 => 4444, 7 => 7777' file && git svn dcommit && - svn up tmp && + svn_cmd up tmp && cd tmp && test x\"\`sed -n -e 4p < file\`\" = x4444 && test x\"\`sed -n -e 7p < file\`\" = x7777 && diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh new file mode 100755 index 0000000000..a4b00f2a3f --- /dev/null +++ b/t/t9138-git-svn-authors-prog.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# +# Copyright (c) 2009 Eric Wong, Mark Lodato +# + +test_description='git svn authors prog tests' + +. ./lib-git-svn.sh + +cat > svn-authors-prog <<'EOF' +#!/usr/bin/perl +$_ = shift; +if (s/-sub$//) { + print "$_ <$_\@sub.example.com>\n"; +} +else { + print "$_ <$_\@example.com>\n"; +} +EOF +chmod +x svn-authors-prog + +cat > svn-authors <<'EOF' +ff = FFFFFFF FFFFFFF <fFf@other.example.com> +EOF + +test_expect_success 'setup svnrepo' ' + for i in aa bb cc-sub dd-sub ee-foo ff + do + svn mkdir -m $i --username $i "$svnrepo"/$i + done + ' + +test_expect_success 'import authors with prog and file' ' + git svn clone --authors-prog=./svn-authors-prog \ + --authors-file=svn-authors "$svnrepo" x + ' + +test_expect_success 'imported 6 revisions successfully' ' + ( + cd x + test "`git rev-list refs/remotes/git-svn | wc -l`" -eq 6 + ) + ' + +test_expect_success 'authors-prog ran correctly' ' + ( + cd x + git rev-list -1 --pretty=raw refs/remotes/git-svn~1 | \ + grep "^author ee-foo <ee-foo@example\.com> " && + git rev-list -1 --pretty=raw refs/remotes/git-svn~2 | \ + grep "^author dd <dd@sub\.example\.com> " && + git rev-list -1 --pretty=raw refs/remotes/git-svn~3 | \ + grep "^author cc <cc@sub\.example\.com> " && + git rev-list -1 --pretty=raw refs/remotes/git-svn~4 | \ + grep "^author bb <bb@example\.com> " && + git rev-list -1 --pretty=raw refs/remotes/git-svn~5 | \ + grep "^author aa <aa@example\.com> " + ) + ' + +test_expect_success 'authors-file overrode authors-prog' ' + ( + cd x + git rev-list -1 --pretty=raw refs/remotes/git-svn | \ + grep "^author FFFFFFF FFFFFFF <fFf@other\.example\.com> " + ) + ' + +test_done diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh new file mode 100755 index 0000000000..f337959ccc --- /dev/null +++ b/t/t9139-git-svn-non-utf8-commitencoding.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# +# Copyright (c) 2009 Eric Wong + +test_description='git svn refuses to dcommit non-UTF8 messages' + +. ./lib-git-svn.sh + +# ISO-2022-JP can pass for valid UTF-8, so skipping that in this test + +for H in ISO8859-1 eucJP +do + test_expect_success "$H setup" ' + mkdir $H && + svn_cmd import -m "$H test" $H "$svnrepo"/$H && + git svn clone "$svnrepo"/$H $H + ' +done + +for H in ISO8859-1 eucJP +do + test_expect_success "$H commit on git side" ' + ( + cd $H && + git config i18n.commitencoding $H && + git checkout -b t refs/remotes/git-svn && + echo $H >F && + git add F && + git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt && + E=$(git cat-file commit HEAD | sed -ne "s/^encoding //p") && + test "z$E" = "z$H" + ) + ' +done + +for H in ISO8859-1 eucJP +do + test_expect_success "$H dcommit to svn" ' + ( + cd $H && + git config --unset i18n.commitencoding && + ! git svn dcommit + ) + ' +done + +test_done diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index 56b7c06921..ef1f8d22f6 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -317,4 +317,22 @@ test_expect_success 'use the same checkout for Git and CVS' ' ' +test_expect_success 're-commit a removed filename which remains in CVS attic' ' + + (cd "$CVSWORK" && + echo >attic_gremlin && + cvs -Q add attic_gremlin && + cvs -Q ci -m "added attic_gremlin" && + rm attic_gremlin && + cvs -Q rm attic_gremlin && + cvs -Q ci -m "removed attic_gremlin") && + + echo > attic_gremlin && + git add attic_gremlin && + git commit -m "Added attic_gremlin" && + git cvsexportcommit -w "$CVSWORK" -c HEAD && + (cd "$CVSWORK"; cvs -Q update -d) && + test -f "$CVSWORK/attic_gremlin" +' + test_done diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh index 8da9ce5459..8c8a9e63c2 100755 --- a/t/t9301-fast-export.sh +++ b/t/t9301-fast-export.sh @@ -68,7 +68,7 @@ test_expect_success 'fast-export master~2..master' ' test_expect_success 'iso-8859-1' ' - git config i18n.commitencoding ISO-8859-1 && + git config i18n.commitencoding ISO8859-1 && # use author and committer name in ISO-8859-1 to match it. . "$TEST_DIRECTORY"/t3901-8859-1.txt && test_tick && diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index f4210fbb04..d539619e89 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -590,7 +590,7 @@ test_expect_success \ echo "ISO-8859-1" >> file && git add file && git config i18n.commitencoding ISO-8859-1 && - git commit -F "$TEST_DIRECTORY"/t3900/ISO-8859-1.txt && + git commit -F "$TEST_DIRECTORY"/t3900/ISO8859-1.txt && git config --unset i18n.commitencoding && gitweb_run "p=.git;a=commit"' test_debug 'cat gitweb.log' diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh index b4ca244626..4eb7d3f7f0 100755 --- a/t/t9700-perl-git.sh +++ b/t/t9700-perl-git.sh @@ -29,6 +29,10 @@ test_expect_success \ git add . && git commit -m "first commit" && + echo "new file in subdir 2" > directory2/file2 && + git add . && + git commit -m "commit in directory2" && + echo "changed file 1" > file1 && git commit -a -m "second commit" && diff --git a/t/t9700/test.pl b/t/t9700/test.pl index 697daf3ffd..6c70aec020 100755 --- a/t/t9700/test.pl +++ b/t/t9700/test.pl @@ -86,15 +86,22 @@ close TEMPFILE; unlink $tmpfile; # paths -is($r->repo_path, "./.git", "repo_path"); +is($r->repo_path, $abs_repo_dir . "/.git", "repo_path"); is($r->wc_path, $abs_repo_dir . "/", "wc_path"); is($r->wc_subdir, "", "wc_subdir initial"); $r->wc_chdir("directory1"); is($r->wc_subdir, "directory1", "wc_subdir after wc_chdir"); -TODO: { - local $TODO = "commands do not work after wc_chdir"; - # Failure output is active even in non-verbose mode and thus - # annoying. Hence we skip these tests as long as they fail. - todo_skip 'config after wc_chdir', 1; - is($r->config("color.string"), "value", "config after wc_chdir"); -} +is($r->config("test.string"), "value", "config after wc_chdir"); + +# Object generation in sub directory +chdir("directory2"); +my $r2 = Git->repository(); +is($r2->repo_path, $abs_repo_dir . "/.git", "repo_path (2)"); +is($r2->wc_path, $abs_repo_dir . "/", "wc_path (2)"); +is($r2->wc_subdir, "directory2/", "wc_subdir initial (2)"); + +# commands in sub directory +my $last_commit = $r2->command_oneline(qw(rev-parse --verify HEAD)); +like($last_commit, qr/^[0-9a-fA-F]{40}$/, 'rev-parse returned hash'); +my $dir_commit = $r2->command_oneline('log', '-n1', '--pretty=format:%H', '.'); +isnt($last_commit, $dir_commit, 'log . does not show last commit'); diff --git a/t/test-lib.sh b/t/test-lib.sh index dad1437fa4..5fdc5d94a2 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -115,7 +115,7 @@ do --tee) shift ;; # was handled already *) - break ;; + echo "error: unknown test option '$1'" >&2; exit 1 ;; esac done @@ -147,7 +147,7 @@ fi error () { say_color error "error: $*" - trap - EXIT + GIT_EXIT_OK=t exit 1 } @@ -179,10 +179,17 @@ test_broken=0 test_success=0 die () { - echo >&5 "FATAL: Unexpected exit with code $?" - exit 1 + code=$? + if test -n "$GIT_EXIT_OK" + then + exit $code + else + echo >&5 "FATAL: Unexpected exit with code $code" + exit 1 + fi } +GIT_EXIT_OK= trap 'die' EXIT # The semantics of the editor variables are that of invoking @@ -285,7 +292,7 @@ test_failure_ () { say_color error "FAIL $test_count: $1" shift echo "$@" | sed -e 's/^/ /' - test "$immediate" = "" || { trap - EXIT; exit 1; } + test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; } } test_known_broken_ok_ () { @@ -347,7 +354,7 @@ test_expect_failure () { then test_known_broken_ok_ "$1" else - test_known_broken_failure_ "$1" + test_known_broken_failure_ "$1" fi fi echo >&3 "" @@ -498,7 +505,7 @@ test_create_repo () { } test_done () { - trap - EXIT + GIT_EXIT_OK=t test_results_dir="$TEST_DIRECTORY/test-results" mkdir -p "$test_results_dir" test_results_path="$test_results_dir/${0%.sh}-$$" @@ -640,7 +647,7 @@ fi test="trash directory.$(basename "$0" .sh)" test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test" rm -fr "$test" || { - trap - EXIT + GIT_EXIT_OK=t echo >&5 "FATAL: Cannot prepare test area" exit 1 } diff --git a/templates/hooks--pre-commit.sample b/templates/hooks--pre-commit.sample index 0ba62076fb..b11ad6a6fb 100755 --- a/templates/hooks--pre-commit.sample +++ b/templates/hooks--pre-commit.sample @@ -7,6 +7,31 @@ # # To enable this hook, rename this file to "pre-commit". +# If you want to allow non-ascii filenames set this variable to true. +allownonascii=$(git config hooks.allownonascii) + +# Cross platform projects tend to avoid non-ascii filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + test "$(git diff --cached --name-only --diff-filter=A -z | + LC_ALL=C tr -d '[ -~]\0')" +then + echo "Error: Attempt to add a non-ascii filename." + echo + echo "This can cause problems if you want to work together" + echo "with people on other platforms than you." + echo + echo "To be portable it is adviseable to rename the file ..." + echo + echo "If you know what you are doing you can disable this" + echo "check using:" + echo + echo " git config hooks.allownonascii true" + echo + exit 1 +fi + if git-rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD diff --git a/templates/hooks--update.sample b/templates/hooks--update.sample index f8bf490cff..fd63b2d662 100755 --- a/templates/hooks--update.sample +++ b/templates/hooks--update.sample @@ -13,6 +13,9 @@ # hooks.allowdeletetag # This boolean sets whether deleting tags will be allowed in the # repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. # hooks.allowdeletebranch # This boolean sets whether deleting branches will be allowed in the # repository. By default they won't be. @@ -44,6 +47,7 @@ allowunannotated=$(git config --bool hooks.allowunannotated) allowdeletebranch=$(git config --bool hooks.allowdeletebranch) denycreatebranch=$(git config --bool hooks.denycreatebranch) allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) # check for no description projectdesc=$(sed -e '1q' "$GIT_DIR/description") @@ -82,6 +86,12 @@ case "$refname","$newrev_type" in ;; refs/tags/*,tag) # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi ;; refs/heads/*,commit) # branch diff --git a/test-parse-options.c b/test-parse-options.c index 61d2c39814..a90bc3003d 100644 --- a/test-parse-options.c +++ b/test-parse-options.c @@ -7,6 +7,7 @@ static unsigned long timestamp; static int abbrev = 7; static int verbose = 0, dry_run = 0, quiet = 0; static char *string = NULL; +static char *file = NULL; int length_callback(const struct option *opt, const char *arg, int unset) { @@ -19,8 +20,15 @@ int length_callback(const struct option *opt, const char *arg, int unset) return 0; } +int number_callback(const struct option *opt, const char *arg, int unset) +{ + *(int *)opt->value = strtol(arg, NULL, 10); + return 0; +} + int main(int argc, const char **argv) { + const char *prefix = "prefix/"; const char *usage[] = { "test-parse-options <options>", NULL @@ -29,6 +37,7 @@ int main(int argc, const char **argv) OPT_BOOLEAN('b', "boolean", &boolean, "get a boolean"), OPT_BIT('4', "or4", &boolean, "bitwise-or boolean with ...0100", 4), + OPT_NEGBIT(0, "neg-or4", &boolean, "same as --no-or4", 4), OPT_GROUP(""), OPT_INTEGER('i', "integer", &integer, "get a integer"), OPT_INTEGER('j', NULL, &integer, "get a integer, too"), @@ -36,6 +45,7 @@ int main(int argc, const char **argv) OPT_DATE('t', NULL, ×tamp, "get timestamp of <time>"), OPT_CALLBACK('L', "length", &integer, "str", "get length of <str>", length_callback), + OPT_FILENAME('F', "file", &file, "set file to <FILE>"), OPT_GROUP("String options"), OPT_STRING('s', "string", &string, "string", "get a string"), OPT_STRING(0, "string2", &string, "str", "get another string"), @@ -45,6 +55,10 @@ int main(int argc, const char **argv) "set string to default", (unsigned long)"default"), OPT_GROUP("Magic arguments"), OPT_ARGUMENT("quux", "means --quux"), + OPT_NUMBER_CALLBACK(&integer, "set integer to NUM", + number_callback), + { OPTION_BOOLEAN, '+', NULL, &boolean, NULL, "same as -b", + PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH }, OPT_GROUP("Standard options"), OPT__ABBREV(&abbrev), OPT__VERBOSE(&verbose), @@ -54,7 +68,7 @@ int main(int argc, const char **argv) }; int i; - argc = parse_options(argc, argv, options, usage, 0); + argc = parse_options(argc, argv, prefix, options, usage, 0); printf("boolean: %d\n", boolean); printf("integer: %u\n", integer); @@ -64,6 +78,7 @@ int main(int argc, const char **argv) printf("verbose: %d\n", verbose); printf("quiet: %s\n", quiet ? "yes" : "no"); printf("dry run: %s\n", dry_run ? "yes" : "no"); + printf("file: %s\n", file ? file : "(not set)"); for (i = 0; i < argc; i++) printf("arg %02d: %s\n", i, argv[i]); diff --git a/transport.c b/transport.c index efecb65258..d8a2392a65 100644 --- a/transport.c +++ b/transport.c @@ -732,9 +732,9 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, str { fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary); if (from) - fprintf(stderr, "%s -> %s", prettify_ref(from), prettify_ref(to)); + fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name)); else - fputs(prettify_ref(to), stderr); + fputs(prettify_refname(to->name), stderr); if (msg) { fputs(" (", stderr); fputs(msg, stderr); @@ -1003,7 +1003,6 @@ int transport_push(struct transport *transport, if (transport->push_refs) { struct ref *remote_refs = transport->get_refs_list(transport, 1); - struct ref **remote_tail; struct ref *local_refs = get_local_heads(); int match_flags = MATCH_REFS_NONE; int verbose = flags & TRANSPORT_PUSH_VERBOSE; @@ -1014,10 +1013,7 @@ int transport_push(struct transport *transport, if (flags & TRANSPORT_PUSH_MIRROR) match_flags |= MATCH_REFS_MIRROR; - remote_tail = &remote_refs; - while (*remote_tail) - remote_tail = &((*remote_tail)->next); - if (match_refs(local_refs, remote_refs, &remote_tail, + if (match_refs(local_refs, &remote_refs, refspec_nr, refspec, match_flags)) { return -1; } @@ -1083,3 +1079,51 @@ int transport_disconnect(struct transport *transport) free(transport); return ret; } + +/* + * Strip username (and password) from an url and return + * it in a newly allocated string. + */ +char *transport_anonymize_url(const char *url) +{ + char *anon_url, *scheme_prefix, *anon_part; + size_t anon_len, prefix_len = 0; + + anon_part = strchr(url, '@'); + if (is_local(url) || !anon_part) + goto literal_copy; + + anon_len = strlen(++anon_part); + scheme_prefix = strstr(url, "://"); + if (!scheme_prefix) { + if (!strchr(anon_part, ':')) + /* cannot be "me@there:/path/name" */ + goto literal_copy; + } else { + const char *cp; + /* make sure scheme is reasonable */ + for (cp = url; cp < scheme_prefix; cp++) { + switch (*cp) { + /* RFC 1738 2.1 */ + case '+': case '.': case '-': + break; /* ok */ + default: + if (isalnum(*cp)) + break; + /* it isn't */ + goto literal_copy; + } + } + /* @ past the first slash does not count */ + cp = strchr(scheme_prefix + 3, '/'); + if (cp && cp < anon_part) + goto literal_copy; + prefix_len = scheme_prefix - url + 3; + } + anon_url = xcalloc(1, 1 + prefix_len + anon_len); + memcpy(anon_url, url, prefix_len); + memcpy(anon_url + prefix_len, anon_part, anon_len); + return anon_url; +literal_copy: + return xstrdup(url); +} diff --git a/transport.h b/transport.h index b1c2252766..27bfc528ac 100644 --- a/transport.h +++ b/transport.h @@ -74,5 +74,6 @@ const struct ref *transport_get_remote_refs(struct transport *transport); int transport_fetch_refs(struct transport *transport, const struct ref *refs); void transport_unlock_pack(struct transport *transport); int transport_disconnect(struct transport *transport); +char *transport_anonymize_url(const char *url); #endif @@ -354,7 +354,7 @@ int is_encoding_utf8(const char *name) * with iconv. If the conversion fails, returns NULL. */ #ifndef NO_ICONV -#ifdef OLD_ICONV +#if defined(OLD_ICONV) || (defined(__sun__) && !defined(_XPG6)) typedef const char * iconv_ibp; #else typedef char * iconv_ibp; diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c index d9737f04c2..1cb65a9516 100644 --- a/xdiff/xmerge.c +++ b/xdiff/xmerge.c @@ -563,23 +563,22 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1, return -1; } status = 0; - if (xscr1 || xscr2) { - if (!xscr1) { - result->ptr = xdl_malloc(mf2->size); - memcpy(result->ptr, mf2->ptr, mf2->size); - result->size = mf2->size; - } else if (!xscr2) { - result->ptr = xdl_malloc(mf1->size); - memcpy(result->ptr, mf1->ptr, mf1->size); - result->size = mf1->size; - } else { - status = xdl_do_merge(&xe1, xscr1, name1, - &xe2, xscr2, name2, - flags, xpp, result); - } - xdl_free_script(xscr1); - xdl_free_script(xscr2); + if (!xscr1) { + result->ptr = xdl_malloc(mf2->size); + memcpy(result->ptr, mf2->ptr, mf2->size); + result->size = mf2->size; + } else if (!xscr2) { + result->ptr = xdl_malloc(mf1->size); + memcpy(result->ptr, mf1->ptr, mf1->size); + result->size = mf1->size; + } else { + status = xdl_do_merge(&xe1, xscr1, name1, + &xe2, xscr2, name2, + flags, xpp, result); } + xdl_free_script(xscr1); + xdl_free_script(xscr2); + xdl_free_env(&xe1); xdl_free_env(&xe2); |