diff options
136 files changed, 2461 insertions, 1179 deletions
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index 0d7fa9cca..b8bf618a3 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -129,3 +129,6 @@ For C programs: used in the git core command set (unless your command is clearly separate from it, such as an importer to convert random-scm-X repositories to git). + + - When we pass <string, length> pair to functions, we should try to + pass them in that order. diff --git a/Documentation/RelNotes-1.6.2.1.txt b/Documentation/RelNotes-1.6.2.1.txt index a33782d47..dfa36416a 100644 --- a/Documentation/RelNotes-1.6.2.1.txt +++ b/Documentation/RelNotes-1.6.2.1.txt @@ -17,9 +17,3 @@ Fixes since v1.6.2 correct .git directory. * git-send-email learned to correctly handle multiple Cc: addresses. - ---- -exec >/var/tmp/1 -O=v1.6.2-21-g7681ed2 -echo O=$(git describe maint) -git shortlog --no-merges $O..maint diff --git a/Documentation/RelNotes-1.6.3.txt b/Documentation/RelNotes-1.6.3.txt index 7f36b6c5c..4353cbf8c 100644 --- a/Documentation/RelNotes-1.6.3.txt +++ b/Documentation/RelNotes-1.6.3.txt @@ -30,8 +30,18 @@ Updates since v1.6.2 (performance) +* many uses of lstat(2) in the codepath for "git checkout" have been + optimized out. + (usability, bells and whistles) +* rsync:/path/to/repo can be used to run git over rsync for local + repositories. It may not be useful in practice; meant primarily for + testing. + +* (msysgit) progress output that is sent over the sideband protocol can + be handled appropriately in Windows console. + * "--pretty=<style>" option to the log family of commands can now be spelled as "--format=<style>". In addition, --format=%formatstring is a short-hand for --pretty=tformat:%formatstring. @@ -47,15 +57,23 @@ Updates since v1.6.2 * git-bisect shows not just the number of remaining commits whose goodness is unknown, but also shows the estimated number of remaining rounds. +* You can give --date=<format> option to git-blame. + * git-branch -r shows HEAD symref that points at a remote branch in interest of each tracked remote repository. * git-config learned -e option to open an editor to edit the config file directly. +* git-clone runs post-checkout hook when run without --no-checkout. + * git-format-patch can be told to use attachment with a new configuration, format.attach. +* git-format-patch can be told to produce deep or shallow message threads. + +* git-grep learned to highlight the found substrings in color. + * git-imap-send learned to work around Thunderbird's inability to easily disable format=flowed with a new configuration, imap.preformattedHTML. @@ -63,6 +81,10 @@ Updates since v1.6.2 descendant of the commit you are rebasing onto with --force-rebase option. +* git-rebase can be told to report diffstat with the --stat option. + +* Output from git-remote command has been vastly improved. + * git-send-email learned --confirm option to review the Cc: list before sending the messages out. @@ -70,6 +92,8 @@ Updates since v1.6.2 * Test scripts can be run under valgrind. +* Makefile learned 'coverage' option to run the test suites with + coverage tracking enabled. Fixes since v1.6.2 ------------------ @@ -80,8 +104,30 @@ release, unless otherwise noted. Here are fixes that this release has, but have not been backported to v1.6.2.X series. +* "git diff --pickaxe-regexp" did not count overlapping matches + correctly (backport by cherry-picking 50fd699). + +* "git-fetch" in a repository that was not cloned from anywhere said + it cannot find 'origin', which was hard to understand for new people. + +* git-gc spent excessive amount of time to decide if an object appears + in a locally existing pack (if needed, backport by merging 69e020a). + +* "git-ls-files --deleted" did not work well with GIT_DIR&GIT_WORK_TREE + (backport by cherry-picking 8ad3dae). + +* "git-read-tree A B C..." without -m option has been broken for a long time + (backport by merging jc/maint-1.6.0-read-tree-overlay) + +* 'git-submodule add' did not tolerate extra slashes and ./ in the + path it accepted from the command line; it now is more lenient + (if needed, backport by merging db75ada). + +* git-send-email ignored --in-reply-to when --no-thread was given + (backport by merging tr/maint-1.6.0-send-email-irt) + --- exec >/var/tmp/1 -O=v1.6.2-77-g8cc3fe4 +O=v1.6.2.1-213-g7d4e3a7 echo O=$(git describe master) git shortlog --no-merges $O..master ^maint diff --git a/Documentation/config.txt b/Documentation/config.txt index 56bd781a1..12540b605 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -25,7 +25,7 @@ blank lines are ignored. The file consists of sections and variables. A section begins with the name of the section in square brackets and continues until the next section begins. Section names are not case sensitive. Only alphanumeric -characters, '`-`' and '`.`' are allowed in section names. Each variable +characters, `-` and `.` are allowed in section names. Each variable must belong to some section, which means that there must be section header before first setting of a variable. @@ -39,7 +39,7 @@ in the section header, like in example below: -------- Subsection names can contain any characters except newline (doublequote -'`"`' and backslash have to be escaped as '`\"`' and '`\\`', +`"` and backslash have to be escaped as `\"` and `\\`, respectively) and are case sensitive. Section header cannot span multiple lines. Variables may belong directly to a section or to a given subsection. You can have `[section]` if you have `[section "subsection"]`, but you @@ -53,7 +53,7 @@ All the other lines are recognized as setting variables, in the form 'name = value'. If there is no equal sign on the line, the entire line is taken as 'name' and the variable is recognized as boolean "true". The variable names are case-insensitive and only alphanumeric -characters and '`-`' are allowed. There can be more than one value +characters and `-` are allowed. There can be more than one value for a given variable; we say then that variable is multivalued. Leading and trailing whitespace in a variable value is discarded. @@ -69,15 +69,15 @@ String values may be entirely or partially enclosed in double quotes. You need to enclose variable value in double quotes if you want to preserve leading or trailing whitespace, or if variable value contains beginning of comment characters (if it contains '#' or ';'). -Double quote '`"`' and backslash '`\`' characters in variable value must -be escaped: use '`\"`' for '`"`' and '`\\`' for '`\`'. +Double quote `"` and backslash `\` characters in variable value must +be escaped: use `\"` for `"` and `\\` for `\`. -The following escape sequences (beside '`\"`' and '`\\`') are recognized: -'`\n`' for newline character (NL), '`\t`' for horizontal tabulation (HT, TAB) -and '`\b`' for backspace (BS). No other char escape sequence, nor octal +The following escape sequences (beside `\"` and `\\`) are recognized: +`\n` for newline character (NL), `\t` for horizontal tabulation (HT, TAB) +and `\b` for backspace (BS). No other char escape sequence, nor octal char sequences are valid. -Variable value ending in a '`\`' is continued on the next line in the +Variable value ending in a `\` is continued on the next line in the customary UNIX fashion. Some variables may require special value format. @@ -221,6 +221,11 @@ core.gitProxy:: Can be overridden by the 'GIT_PROXY_COMMAND' environment variable (which always applies universally, without the special "for" handling). ++ +The special string `none` can be used as the proxy command to +specify that no proxy be used for a given domain pattern. +This is useful for excluding servers inside a firewall from +proxy use, while defaulting to a common proxy for external domains. core.ignoreStat:: If true, commands which modify both the working tree and the index @@ -382,9 +387,9 @@ core.pager:: to override git's default settings this way, you need to be explicit. For example, to disable the S option in a backward compatible manner, set `core.pager` - to "`less -+$LESS -FRX`". This will be passed to the + to `less -+$LESS -FRX`. This will be passed to the shell by git, which will translate the final command to - "`LESS=FRSX less -+FRSX -FRX`". + `LESS=FRSX less -+FRSX -FRX`. core.whitespace:: A comma separated list of common whitespace problems to @@ -548,6 +553,25 @@ color.diff.<slot>:: whitespace errors). The values of these variables may be specified as in color.branch.<slot>. +color.grep:: + When set to `always`, always highlight matches. When `false` (or + `never`), never. When set to `true` or `auto`, use color only + when the output is written to the terminal. Defaults to `false`. + +color.grep.external:: + The string value of this variable is passed to an external 'grep' + command as a command line option if match highlighting is turned + on. If set to an empty string, no option is passed at all, + turning off coloring for external 'grep' calls; this is the default. + For GNU grep, set it to `--color=always` to highlight matches even + when a pager is used. + +color.grep.match:: + Use customized color for matches. The value of this variable + may be specified as in color.branch.<slot>. It is passed using + the environment variables 'GREP_COLOR' and 'GREP_COLORS' when + calling an external 'grep'. + color.interactive:: When set to `always`, always use colors for interactive prompts and displays (such as those used by "git-add --interactive"). @@ -1161,7 +1185,7 @@ pager.<cmd>:: particular git subcommand when writing to a tty. If `\--paginate` or `\--no-pager` is specified on the command line, it takes precedence over this option. To disable pagination for - all commands, set `core.pager` or 'GIT_PAGER' to "`cat`". + all commands, set `core.pager` or `GIT_PAGER` to `cat`. pull.octopus:: The default merge strategy to use when pulling multiple branches diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 6103d62fe..27b73bcf9 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -76,8 +76,8 @@ OPTIONS based sha1 expressions such as "<branchname>@\{yesterday}". -f:: - Force the creation of a new branch even if it means deleting - a branch that already exists with the same name. + Reset <branchname> to <startpoint> if <branchname> exists + already. Without `-f` 'git-branch' refuses to change an existing branch. -m:: Move/rename a branch and the corresponding reflog. diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 125d8f3c3..1a6c19e5c 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -133,9 +133,9 @@ the conflicted merge in the specified paths. When this parameter names a non-branch (but still a valid commit object), your HEAD becomes 'detached'. + -As a special case, the "`@\{-N\}`" syntax for the N-th last branch +As a special case, the `"@\{-N\}"` syntax for the N-th last branch checks out the branch (instead of detaching). You may also specify -"`-`" which is synonymous with "`@\{-1\}`". +`-` which is synonymous with `"@\{-1\}"`. Detached HEAD diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 82ce89eae..7131ee3c6 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -11,7 +11,7 @@ SYNOPSIS [verse] 'git config' [<file-option>] [type] [-z|--null] name [value [value_regex]] 'git config' [<file-option>] [type] --add name value -'git config' [<file-option>] [type] --replace-all name [value [value_regex]] +'git config' [<file-option>] [type] --replace-all name value [value_regex] 'git config' [<file-option>] [type] [-z|--null] --get name [value_regex] 'git config' [<file-option>] [type] [-z|--null] --get-all name [value_regex] 'git config' [<file-option>] [type] [-z|--null] --get-regexp name_regex [value_regex] diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 7ffe03f42..237f85e76 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -91,7 +91,9 @@ OPTIONS --index-filter <command>:: This is the filter for rewriting the index. It is similar to the tree filter but does not check out the tree, which makes it much - faster. For hairy cases, see linkgit:git-update-index[1]. + faster. Frequently used with `git rm \--cached + \--ignore-unmatch ...`, see EXAMPLES below. For hairy + cases, see linkgit:git-update-index[1]. --parent-filter <command>:: This is the filter for rewriting the commit's parent list. @@ -204,19 +206,18 @@ However, if the file is absent from the tree of some commit, a simple `rm filename` will fail for that tree and commit. Thus you may instead want to use `rm -f filename` as the script. -A significantly faster version: +Using `\--index-filter` with 'git-rm' yields a significantly faster +version. Like with using `rm filename`, `git rm --cached filename` +will fail if the file is absent from the tree of a commit. If you +want to "completely forget" a file, it does not matter when it entered +history, so we also add `\--ignore-unmatch`: -------------------------------------------------------------------------- -git filter-branch --index-filter 'git rm --cached filename' HEAD +git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD -------------------------------------------------------------------------- Now, you will get the rewritten history saved in HEAD. -As with using `rm filename`, `git rm --cached filename` will fail -if the file is absent from the tree of a commit. If it is not important -whether the file is already absent from the tree, you can use -`git rm --cached --ignore-unmatch filename` instead. - To rewrite the repository to look as if `foodir/` had been its project root, and discard all other history: diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt index 553da6cbb..fccb82deb 100644 --- a/Documentation/git-grep.txt +++ b/Documentation/git-grep.txt @@ -17,6 +17,7 @@ SYNOPSIS [-l | --files-with-matches] [-L | --files-without-match] [-z | --null] [-c | --count] [--all-match] + [--color | --no-color] [-A <post-context>] [-B <pre-context>] [-C <context>] [-f <file>] [-e] <pattern> [--and|--or|--not|(|)|-e <pattern>...] [<tree>...] @@ -105,6 +106,13 @@ OPTIONS Instead of showing every matched line, show the number of lines that match. +--color:: + Show colored matches. + +--no-color:: + Turn off match highlighting, even when the configuration file + gives the default to color output. + -[ABC] <context>:: Show `context` trailing (`A` -- after), or leading (`B` -- before), or both (`C` -- context) lines, and place a diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index f7be5846a..cc0d30fe7 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -146,7 +146,7 @@ And here is another line that is cleanly resolved or unmodified. ------------ The area where a pair of conflicting changes happened is marked with markers -"`<<<<<<<`", "`=======`", and "`>>>>>>>`". The part before the "`=======`" +`<<<<<<<`, `=======`, and `>>>>>>>`. The part before the `=======` is typically your side, and the part afterwards is typically their side. The default format does not show what the original said in the conflicting @@ -173,8 +173,8 @@ Git makes conflict resolution easy. And here is another line that is cleanly resolved or unmodified. ------------ -In addition to the "`<<<<<<<`", "`=======`", and "`>>>>>>>`" markers, it uses -another "`|||||||`" marker that is followed by the original text. You can +In addition to the `<<<<<<<`, `=======`, and `>>>>>>>` markers, it uses +another `|||||||` marker that is followed by the original text. You can tell that the original just stated a fact, and your side simply gave in to that statement and gave up, while the other side tried to have a more positive attitude. You can sometimes come up with a better resolution by diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 4e7e5a719..fd53c49fb 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -24,8 +24,8 @@ every time you push into it, by setting up 'hooks' there. See documentation for linkgit:git-receive-pack[1]. -OPTIONS -------- +OPTIONS[[OPTIONS]] +------------------ <repository>:: The "remote" repository that is destination of a push operation. This parameter can be either a URL @@ -187,6 +187,28 @@ reason:: Examples -------- +git push:: + Works like `git push <remote>`, where <remote> is the + current branch's remote (or `origin`, if no remote is + configured for the current branch). + +git push origin:: + Without additional configuration, works like + `git push origin :`. ++ +The default behavior of this command when no <refspec> is given can be +configured by setting the `push` option of the remote. ++ +For example, to default to pushing only the current branch to `origin` +use `git config remote.origin.push HEAD`. Any valid <refspec> (like +the ones in the examples below) can be configured as the default for +`git push origin`. + +git push origin ::: + Push "matching" branches to `origin`. See + <refspec> in the <<OPTIONS,OPTIONS>> section above for a + description of "matching" branches. + git push origin master:: Find a ref that matches `master` in the source repository (most likely, it would find `refs/heads/master`), and update diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 57bd333f0..3d5a066c3 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -258,11 +258,23 @@ OPTIONS context exist they all must match. By default no context is ever ignored. +-f:: +--force-rebase:: + Force the rebase even if the current branch is a descendant + of the commit you are rebasing onto. Normally the command will + exit with the message "Current branch is up to date" in such a + situation. + --whitespace=<option>:: This flag is passed to the 'git-apply' program (see linkgit:git-apply[1]) that applies the patch. Incompatible with the --interactive option. +--committer-date-is-author-date:: +--ignore-date:: + These flags are passed to 'git-am' to easily change the dates + of the rebased commits (see linkgit:git-am[1]). + -i:: --interactive:: Make a list of the commits which are about to be rebased. Let the diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index fad983e29..c9c0e6f93 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -13,6 +13,7 @@ SYNOPSIS 'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url> 'git remote rename' <old> <new> 'git remote rm' <name> +'git remote set-head' <name> [-a | -d | <branch>] 'git remote show' [-n] <name> 'git remote prune' [-n | --dry-run] <name> 'git remote update' [group] @@ -53,8 +54,7 @@ is created. You can give more than one `-t <branch>` to track multiple branches without grabbing all branches. + With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set -up to point at remote's `<master>` branch instead of whatever -branch the `HEAD` at the remote repository actually points at. +up to point at remote's `<master>` branch. See also the set-head command. + In mirror mode, enabled with `\--mirror`, the refs will not be stored in the 'refs/remotes/' namespace, but in 'refs/heads/'. This option @@ -76,6 +76,30 @@ the configuration file format. Remove the remote named <name>. All remote tracking branches and configuration settings for the remote are removed. +'set-head':: + +Sets or deletes the default branch (`$GIT_DIR/remotes/<name>/HEAD`) for +the named remote. Having a default branch for a remote is not required, +but allows the name of the remote to be specified in lieu of a specific +branch. For example, if the default branch for `origin` is set to +`master`, then `origin` may be specified wherever you would normally +specify `origin/master`. ++ +With `-d`, `$GIT_DIR/remotes/<name>/HEAD` is deleted. ++ +With `-a`, the remote is queried to determine its `HEAD`, then +`$GIT_DIR/remotes/<name>/HEAD` is set to the same branch. e.g., if the remote +`HEAD` is pointed at `next`, "`git remote set-head origin -a`" will set +`$GIT_DIR/refs/remotes/origin/HEAD` to `refs/remotes/origin/next`. This will +only work if `refs/remotes/origin/next` already exists; if not it must be +fetched first. ++ +Use `<branch>` to set `$GIT_DIR/remotes/<name>/HEAD` explicitly. e.g., "git +remote set-head origin master" will set `$GIT_DIR/refs/remotes/origin/HEAD` to +`refs/remotes/origin/master`. This will only work if +`refs/remotes/origin/master` already exists; if not it must be fetched first. ++ + 'show':: Gives some information about the remote <name>. diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 3ccef2f2b..5ed2bc840 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -299,18 +299,18 @@ previous section means the set of commits reachable from that commit, following the commit ancestry chain. To exclude commits reachable from a commit, a prefix `{caret}` -notation is used. E.g. "`{caret}r1 r2`" means commits reachable +notation is used. E.g. `{caret}r1 r2` means commits reachable from `r2` but exclude the ones reachable from `r1`. This set operation appears so often that there is a shorthand for it. When you have two commits `r1` and `r2` (named according to the syntax explained in SPECIFYING REVISIONS above), you can ask for commits that are reachable from r2 excluding those that are reachable -from r1 by "`{caret}r1 r2`" and it can be written as "`r1..r2`". +from r1 by `{caret}r1 r2` and it can be written as `r1..r2`. -A similar notation "`r1\...r2`" is called symmetric difference +A similar notation `r1\...r2` is called symmetric difference of `r1` and `r2` and is defined as -"`r1 r2 --not $(git merge-base --all r1 r2)`". +`r1 r2 --not $(git merge-base --all r1 r2)`. It is the set of commits that are reachable from either one of `r1` or `r2` but not from both. diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 14dfb501e..10dfd667b 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -60,14 +60,13 @@ The --cc option must be repeated for each user you want on the cc list. Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an introductory message for the patch series. + -When '--compose' is used, git send-email gets less interactive will use the -values of the headers you set there. If the body of the email (what you type -after the headers and a blank line) only contains blank (or GIT: prefixed) -lines, the summary won't be sent, but git-send-email will still use the -Headers values if you don't removed them. +When '--compose' is used, git send-email will use the From, Subject, and +In-Reply-To headers specified in the message. If the body of the message +(what you type after the headers and a blank line) only contains blank +(or GIT: prefixed) lines the summary won't be sent, but From, Subject, +and In-Reply-To headers will be used unless they are removed. + -If it wasn't able to see a header in the summary it will ask you about it -interactively after quitting your editor. +Missing From or In-Reply-To headers will be prompted for. --from:: Specify the sender of the emails. This will default to diff --git a/Documentation/git.txt b/Documentation/git.txt index 9a26bde73..7513c57c6 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.6.2/git.html[documentation for release 1.6.2] +* link:v1.6.2.1/git.html[documentation for release 1.6.2.1] * release notes for + link:RelNotes-1.6.2.1.txt[1.6.2.1], link:RelNotes-1.6.2.txt[1.6.2]. * link:v1.6.1.3/git.html[documentation for release 1.6.1.3] diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt index 29e5929db..be39ed7c1 100644 --- a/Documentation/gitcli.txt +++ b/Documentation/gitcli.txt @@ -46,20 +46,20 @@ Here are the rules regarding the "flags" that you should follow when you are scripting git: * it's preferred to use the non dashed form of git commands, which means that - you should prefer `"git foo"` to `"git-foo"`. + you should prefer `git foo` to `git-foo`. - * splitting short options to separate words (prefer `"git foo -a -b"` - to `"git foo -ab"`, the latter may not even work). + * splitting short options to separate words (prefer `git foo -a -b` + to `git foo -ab`, the latter may not even work). * when a command line option takes an argument, use the 'sticked' form. In - other words, write `"git foo -oArg"` instead of `"git foo -o Arg"` for short - options, and `"git foo --long-opt=Arg"` instead of `"git foo --long-opt Arg"` + other words, write `git foo -oArg` instead of `git foo -o Arg` for short + options, and `git foo --long-opt=Arg` instead of `git foo --long-opt Arg` for long options. An option that takes optional option-argument must be written in the 'sticked' form. * when you give a revision parameter to a command, make sure the parameter is not ambiguous with a name of a file in the work tree. E.g. do not write - `"git log -1 HEAD"` but write `"git log -1 HEAD --"`; the former will not work + `git log -1 HEAD` but write `git log -1 HEAD --`; the former will not work if you happen to have a file called `HEAD` in the work tree. @@ -99,17 +99,17 @@ usage: git-describe [options] <committish>* Negating options ~~~~~~~~~~~~~~~~ -Options with long option names can be negated by prefixing `"--no-"`. For -example, `"git branch"` has the option `"--track"` which is 'on' by default. You -can use `"--no-track"` to override that behaviour. The same goes for `"--color"` -and `"--no-color"`. +Options with long option names can be negated by prefixing `--no-`. For +example, `git branch` has the option `--track` which is 'on' by default. You +can use `--no-track` to override that behaviour. The same goes for `--color` +and `--no-color`. Aggregating short options ~~~~~~~~~~~~~~~~~~~~~~~~~ Commands that support the enhanced option parser allow you to aggregate short -options. This means that you can for example use `"git rm -rf"` or -`"git clean -fdx"`. +options. This means that you can for example use `git rm -rf` or +`git clean -fdx`. Separating argument from the option diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt index 9afca755e..4fc1cf118 100644 --- a/Documentation/glossary-content.txt +++ b/Documentation/glossary-content.txt @@ -262,7 +262,7 @@ This commit is referred to as a "merge commit", or sometimes just a 'origin' is used for that purpose. New upstream updates will be fetched into remote <<def_tracking_branch,tracking branches>> named origin/name-of-upstream-branch, which you can see using - "`git branch -r`". + `git branch -r`. [[def_pack]]pack:: A set of objects which have been compressed into one file (to save space diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 96af8977f..e33b29b1d 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1136,10 +1136,10 @@ Ignoring files A project will often generate files that you do 'not' want to track with git. This typically includes files generated by a build process or temporary backup files made by your editor. Of course, 'not' tracking files with git -is just a matter of 'not' calling "`git-add`" on them. But it quickly becomes +is just a matter of 'not' calling `git-add` on them. But it quickly becomes annoying to have these untracked files lying around; e.g. they make -"`git add .`" practically useless, and they keep showing up in the output of -"`git status`". +`git add .` practically useless, and they keep showing up in the output of +`git status`. You can tell git to ignore certain files by creating a file called .gitignore in the top level of your working directory, with contents such as: @@ -126,6 +126,12 @@ all:: # randomly break unless your underlying filesystem supports those sub-second # times (my ext3 doesn't). # +# Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of +# "st_ctim" +# +# Define NO_NSEC if your "struct stat" does not have "st_ctim.tv_nsec" +# available. This automatically turns USE_NSEC off. +# # Define USE_STDEV below if you want git to care about the underlying device # change being considered an inode change from the update-index perspective. # @@ -657,6 +663,7 @@ ifeq ($(uname_S),Darwin) endif NO_MEMMEM = YesPlease THREADED_DELTA_SEARCH = YesPlease + USE_ST_TIMESPEC = YesPlease endif ifeq ($(uname_S),SunOS) NEEDS_SOCKET = YesPlease @@ -734,6 +741,7 @@ ifeq ($(uname_S),AIX) NO_MEMMEM = YesPlease NO_MKDTEMP = YesPlease NO_STRLCPY = YesPlease + NO_NSEC = YesPlease FREAD_READS_DIRECTORIES = UnfortunatelyYes INTERNAL_QSORT = UnfortunatelyYes NEEDS_LIBICONV=YesPlease @@ -776,7 +784,6 @@ ifneq (,$(findstring CYGWIN,$(uname_S))) COMPAT_OBJS += compat/cygwin.o endif ifneq (,$(findstring MINGW,$(uname_S))) - NO_MMAP = YesPlease NO_PREAD = YesPlease NO_OPENSSL = YesPlease NO_CURL = YesPlease @@ -799,6 +806,8 @@ ifneq (,$(findstring MINGW,$(uname_S))) RUNTIME_PREFIX = YesPlease NO_POSIX_ONLY_PROGRAMS = YesPlease NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease + NO_NSEC = YesPlease + USE_WIN32_MMAP = YesPlease COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\" @@ -920,6 +929,15 @@ endif ifdef NO_ST_BLOCKS_IN_STRUCT_STAT BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT endif +ifdef USE_NSEC + BASIC_CFLAGS += -DUSE_NSEC +endif +ifdef USE_ST_TIMESPEC + BASIC_CFLAGS += -DUSE_ST_TIMESPEC +endif +ifdef NO_NSEC + BASIC_CFLAGS += -DNO_NSEC +endif ifdef NO_C99_FORMAT BASIC_CFLAGS += -DNO_C99_FORMAT endif @@ -967,6 +985,11 @@ endif ifdef NO_MMAP COMPAT_CFLAGS += -DNO_MMAP COMPAT_OBJS += compat/mmap.o +else + ifdef USE_WIN32_MMAP + COMPAT_CFLAGS += -DUSE_WIN32_MMAP + COMPAT_OBJS += compat/win32mmap.o + endif endif ifdef NO_PREAD COMPAT_CFLAGS += -DNO_PREAD @@ -1363,6 +1386,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@ @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@ + @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@ ### Detect Tck/Tk interpreter path changes ifndef NO_TCLTK @@ -32,21 +32,59 @@ static int find_tracked_branch(struct remote *remote, void *priv) return 0; } -static int should_setup_rebase(const struct tracking *tracking) +static int should_setup_rebase(const char *origin) { switch (autorebase) { case AUTOREBASE_NEVER: return 0; case AUTOREBASE_LOCAL: - return tracking->remote == NULL; + return origin == NULL; case AUTOREBASE_REMOTE: - return tracking->remote != NULL; + return origin != NULL; case AUTOREBASE_ALWAYS: return 1; } return 0; } +void install_branch_config(int flag, const char *local, const char *origin, const char *remote) +{ + struct strbuf key = STRBUF_INIT; + int rebasing = should_setup_rebase(origin); + + strbuf_addf(&key, "branch.%s.remote", local); + git_config_set(key.buf, origin ? origin : "."); + + strbuf_reset(&key); + strbuf_addf(&key, "branch.%s.merge", local); + git_config_set(key.buf, remote); + + if (rebasing) { + strbuf_reset(&key); + strbuf_addf(&key, "branch.%s.rebase", local); + git_config_set(key.buf, "true"); + } + + if (flag & BRANCH_CONFIG_VERBOSE) { + strbuf_reset(&key); + + strbuf_addstr(&key, origin ? "remote" : "local"); + + /* Are we tracking a proper "branch"? */ + if (!prefixcmp(remote, "refs/heads/")) { + strbuf_addf(&key, " branch %s", remote + 11); + if (origin) + strbuf_addf(&key, " from %s", origin); + } + else + strbuf_addf(&key, " ref %s", remote); + printf("Branch %s set up to track %s%s.\n", + local, key.buf, + rebasing ? " by rebasing" : ""); + } + strbuf_release(&key); +} + /* * This is called when new_ref is branched off of orig_ref, and tries * to infer the settings for branch.<new_ref>.{remote,merge} from the @@ -55,7 +93,6 @@ static int should_setup_rebase(const struct tracking *tracking) static int setup_tracking(const char *new_ref, const char *orig_ref, enum branch_track track) { - char key[1024]; struct tracking tracking; if (strlen(new_ref) > 1024 - 7 - 7 - 1) @@ -80,19 +117,10 @@ static int setup_tracking(const char *new_ref, const char *orig_ref, return error("Not tracking: ambiguous information for ref %s", orig_ref); - sprintf(key, "branch.%s.remote", new_ref); - git_config_set(key, tracking.remote ? tracking.remote : "."); - sprintf(key, "branch.%s.merge", new_ref); - git_config_set(key, tracking.src ? tracking.src : orig_ref); - printf("Branch %s set up to track %s branch %s.\n", new_ref, - tracking.remote ? "remote" : "local", orig_ref); - if (should_setup_rebase(&tracking)) { - sprintf(key, "branch.%s.rebase", new_ref); - git_config_set(key, "true"); - printf("This branch will rebase on pull.\n"); - } - free(tracking.src); + install_branch_config(BRANCH_CONFIG_VERBOSE, new_ref, tracking.remote, + tracking.src ? tracking.src : orig_ref); + free(tracking.src); return 0; } @@ -21,4 +21,11 @@ void create_branch(const char *head, const char *name, const char *start_name, */ void remove_branch_state(void); +/* + * Configure local branch "local" to merge remote branch "remote" + * taken from origin "origin". + */ +#define BRANCH_CONFIG_VERBOSE 01 +extern void install_branch_config(int flag, const char *local, const char *origin, const char *remote); + #endif diff --git a/builtin-add.c b/builtin-add.c index 08443f2f1..7ddb65932 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -148,7 +148,7 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p if (pathspec) { const char **p; for (p = pathspec; *p; p++) { - if (has_symlink_leading_path(strlen(*p), *p)) { + if (has_symlink_leading_path(*p, strlen(*p))) { int len = prefix ? strlen(prefix) : 0; die("'%s' is beyond a symbolic link", *p + len); } diff --git a/builtin-apply.c b/builtin-apply.c index f312798af..b52aa20cf 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2360,7 +2360,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists) * In such a case, path "new_name" does not exist as * far as git is concerned. */ - if (has_symlink_leading_path(strlen(new_name), new_name)) + if (has_symlink_leading_path(new_name, strlen(new_name))) return 0; return error("%s: already exists in working directory", new_name); @@ -3212,7 +3212,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) struct option builtin_apply_options[] = { { OPTION_CALLBACK, 0, "exclude", NULL, "path", - "don´t apply changes matching the given path", + "don't apply changes matching the given path", 0, option_parse_exclude }, { OPTION_CALLBACK, 0, "include", NULL, "path", "apply changes matching the given path", @@ -3224,10 +3224,10 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) "ignore additions made by the patch"), OPT_BOOLEAN(0, "stat", &diffstat, "instead of applying the patch, output diffstat for the input"), - OPT_BOOLEAN(0, "allow-binary-replacement", &binary, - "now no-op"), - OPT_BOOLEAN(0, "binary", &binary, - "now no-op"), + { OPTION_BOOLEAN, 0, "allow-binary-replacement", &binary, + NULL, "old option, now no-op", PARSE_OPT_HIDDEN }, + { OPTION_BOOLEAN, 0, "binary", &binary, + NULL, "old option, now no-op", PARSE_OPT_HIDDEN }, OPT_BOOLEAN(0, "numstat", &numstat, "shows number of added and deleted lines in decimal notation"), OPT_BOOLEAN(0, "summary", &summary, diff --git a/builtin-archive.c b/builtin-archive.c index 60adef936..ab50cebba 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -52,7 +52,7 @@ static int run_remote_archiver(int argc, const char **argv, die("git archive: expected a flush"); /* Now, start reading from fd[0] and spit it out to stdout */ - rv = recv_sideband("archive", fd[0], 1, 2); + rv = recv_sideband("archive", fd[0], 1); close(fd[0]); close(fd[1]); rv |= finish_connect(conn); diff --git a/builtin-clone.c b/builtin-clone.c index 39523cee3..0031b5f51 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -20,6 +20,8 @@ #include "dir.h" #include "pack-refs.h" #include "sigchain.h" +#include "branch.h" +#include "remote.h" #include "run-command.h" /* @@ -294,43 +296,6 @@ static void remove_junk_on_signal(int signo) raise(signo); } -static const struct ref *locate_head(const struct ref *refs, - const struct ref *mapped_refs, - const struct ref **remote_head_p) -{ - const struct ref *remote_head = NULL; - const struct ref *remote_master = NULL; - const struct ref *r; - for (r = refs; r; r = r->next) - if (!strcmp(r->name, "HEAD")) - remote_head = r; - - for (r = mapped_refs; r; r = r->next) - if (!strcmp(r->name, "refs/heads/master")) - remote_master = r; - - if (remote_head_p) - *remote_head_p = remote_head; - - /* If there's no HEAD value at all, never mind. */ - if (!remote_head) - return NULL; - - /* If refs/heads/master could be right, it is. */ - if (remote_master && !hashcmp(remote_master->old_sha1, - remote_head->old_sha1)) - return remote_master; - - /* Look for another ref that points there */ - for (r = mapped_refs; r; r = r->next) - if (r != remote_head && - !hashcmp(r->old_sha1, remote_head->old_sha1)) - return r; - - /* Nothing is the same */ - return NULL; -} - static struct ref *write_remote_refs(const struct ref *refs, struct refspec *refspec, const char *reflog) { @@ -351,19 +316,6 @@ static struct ref *write_remote_refs(const struct ref *refs, return local_refs; } -static void install_branch_config(const char *local, - const char *origin, - const char *remote) -{ - struct strbuf key = STRBUF_INIT; - strbuf_addf(&key, "branch.%s.remote", local); - git_config_set(key.buf, origin); - strbuf_reset(&key); - strbuf_addf(&key, "branch.%s.merge", local); - git_config_set(key.buf, remote); - strbuf_release(&key); -} - int cmd_clone(int argc, const char **argv, const char *prefix) { int is_bundle = 0; @@ -378,7 +330,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) char *src_ref_prefix = "refs/heads/"; int err = 0; - struct refspec refspec; + struct refspec *refspec; + const char *fetch_pattern; junk_pid = getpid(); @@ -483,8 +436,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix) strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin); } + strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf); + if (option_mirror || !option_bare) { /* Configure the remote */ + strbuf_addf(&key, "remote.%s.fetch", option_origin); + git_config_set_multivar(key.buf, value.buf, "^$", 0); + strbuf_reset(&key); + if (option_mirror) { strbuf_addf(&key, "remote.%s.mirror", option_origin); git_config_set(key.buf, "true"); @@ -493,19 +452,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix) strbuf_addf(&key, "remote.%s.url", option_origin); git_config_set(key.buf, repo); - strbuf_reset(&key); - - strbuf_addf(&key, "remote.%s.fetch", option_origin); - strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf); - git_config_set_multivar(key.buf, value.buf, "^$", 0); strbuf_reset(&key); - strbuf_reset(&value); } - refspec.force = 0; - refspec.pattern = 1; - refspec.src = src_ref_prefix; - refspec.dst = branch_top.buf; + fetch_pattern = value.buf; + refspec = parse_fetch_refspec(1, &fetch_pattern); + + strbuf_reset(&value); if (path && !is_bundle) refs = clone_local(path, git_dir); @@ -539,9 +492,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (refs) { clear_extra_refs(); - mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf); + mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf); - head_points_at = locate_head(refs, mapped_refs, &remote_head); + remote_head = find_ref_by_name(refs, "HEAD"); + head_points_at = guess_remote_head(remote_head, mapped_refs, 0); } else { warning("You appear to have cloned an empty repository."); @@ -549,7 +503,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) remote_head = NULL; option_no_checkout = 1; if (!option_bare) - install_branch_config("master", option_origin, + install_branch_config(0, "master", option_origin, "refs/heads/master"); } @@ -579,7 +533,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) head_points_at->peer_ref->name, reflog_msg.buf); - install_branch_config(head, option_origin, + install_branch_config(0, head, option_origin, head_points_at->name); } } else if (remote_head) { diff --git a/builtin-config.c b/builtin-config.c index d52a05744..d8da72cf2 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -1,9 +1,12 @@ #include "builtin.h" #include "cache.h" #include "color.h" +#include "parse-options.h" -static const char git_config_set_usage[] = -"git config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int | --bool-or-int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty] | --edit | -e ]"; +static const char *const builtin_config_usage[] = { + "git config [options]", + NULL +}; static char *key; static regex_t *key_regexp; @@ -16,7 +19,67 @@ static int seen; static char delim = '='; static char key_delim = ' '; static char term = '\n'; -static enum { T_RAW, T_INT, T_BOOL, T_BOOL_OR_INT } type = T_RAW; + +static int use_global_config, use_system_config; +static const char *given_config_file; +static int actions, types; +static const char *get_color_slot, *get_colorbool_slot; +static int end_null; + +#define ACTION_GET (1<<0) +#define ACTION_GET_ALL (1<<1) +#define ACTION_GET_REGEXP (1<<2) +#define ACTION_REPLACE_ALL (1<<3) +#define ACTION_ADD (1<<4) +#define ACTION_UNSET (1<<5) +#define ACTION_UNSET_ALL (1<<6) +#define ACTION_RENAME_SECTION (1<<7) +#define ACTION_REMOVE_SECTION (1<<8) +#define ACTION_LIST (1<<9) +#define ACTION_EDIT (1<<10) +#define ACTION_SET (1<<11) +#define ACTION_SET_ALL (1<<12) +#define ACTION_GET_COLOR (1<<13) +#define ACTION_GET_COLORBOOL (1<<14) + +#define TYPE_BOOL (1<<0) +#define TYPE_INT (1<<1) +#define TYPE_BOOL_OR_INT (1<<2) + +static struct option builtin_config_options[] = { + OPT_GROUP("Config file location"), + OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"), + OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"), + OPT_STRING('f', "file", &given_config_file, "FILE", "use given config file"), + OPT_GROUP("Action"), + OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET), + OPT_BIT(0, "get-all", &actions, "get all values: key [value-regex]", ACTION_GET_ALL), + OPT_BIT(0, "get-regexp", &actions, "get values for regexp: name-regex [value-regex]", ACTION_GET_REGEXP), + OPT_BIT(0, "replace-all", &actions, "replace all matching variables: name value [value_regex]", ACTION_REPLACE_ALL), + OPT_BIT(0, "add", &actions, "adds a new variable: name value", ACTION_ADD), + OPT_BIT(0, "unset", &actions, "removes a variable: name [value-regex]", ACTION_UNSET), + OPT_BIT(0, "unset-all", &actions, "removes all matches: name [value-regex]", ACTION_UNSET_ALL), + OPT_BIT(0, "rename-section", &actions, "rename section: old-name new-name", ACTION_RENAME_SECTION), + OPT_BIT(0, "remove-section", &actions, "remove a section: name", ACTION_REMOVE_SECTION), + OPT_BIT('l', "list", &actions, "list all", ACTION_LIST), + OPT_BIT('e', "edit", &actions, "opens an editor", ACTION_EDIT), + OPT_STRING(0, "get-color", &get_color_slot, "slot", "find the color configured: [default]"), + OPT_STRING(0, "get-colorbool", &get_colorbool_slot, "slot", "find the color setting: [stdout-is-tty]"), + OPT_GROUP("Type"), + OPT_BIT(0, "bool", &types, "value is \"true\" or \"false\"", TYPE_BOOL), + OPT_BIT(0, "int", &types, "value is decimal number", TYPE_INT), + OPT_BIT(0, "bool-or-int", &types, "value is --bool or --int", TYPE_BOOL_OR_INT), + OPT_GROUP("Other"), + OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"), + OPT_END(), +}; + +static void check_argc(int argc, int min, int max) { + if (argc >= min && argc <= max) + return; + error("wrong number of arguments"); + usage_with_options(builtin_config_usage, builtin_config_options); +} static int show_all_config(const char *key_, const char *value_, void *cb) { @@ -49,11 +112,11 @@ static int show_config(const char *key_, const char *value_, void *cb) } if (seen && !do_all) dup_error = 1; - if (type == T_INT) + if (types == TYPE_INT) sprintf(value, "%d", git_config_int(key_, value_?value_:"")); - else if (type == T_BOOL) + else if (types == TYPE_BOOL) vptr = git_config_bool(key_, value_) ? "true" : "false"; - else if (type == T_BOOL_OR_INT) { + else if (types == TYPE_BOOL_OR_INT) { int is_bool, v; v = git_config_bool_or_int(key_, value_, &is_bool); if (is_bool) @@ -152,18 +215,18 @@ static char *normalize_value(const char *key, const char *value) if (!value) return NULL; - if (type == T_RAW) + if (types == 0) normalized = xstrdup(value); else { normalized = xmalloc(64); - if (type == T_INT) { + if (types == TYPE_INT) { int v = git_config_int(key, value); sprintf(normalized, "%d", v); } - else if (type == T_BOOL) + else if (types == TYPE_BOOL) sprintf(normalized, "%s", git_config_bool(key, value) ? "true" : "false"); - else if (type == T_BOOL_OR_INT) { + else if (types == TYPE_BOOL_OR_INT) { int is_bool, v; v = git_config_bool_or_int(key, value, &is_bool); if (!is_bool) @@ -178,6 +241,7 @@ static char *normalize_value(const char *key, const char *value) static int get_color_found; static const char *get_color_slot; +static const char *get_colorbool_slot; static char parsed_color[COLOR_MAXLEN]; static int git_get_color_config(const char *var, const char *value, void *cb) @@ -191,29 +255,8 @@ static int git_get_color_config(const char *var, const char *value, void *cb) return 0; } -static int get_color(int argc, const char **argv) +static void get_color(const char *def_color) { - /* - * grab the color setting for the given slot from the configuration, - * or parse the default value if missing, and return ANSI color - * escape sequence. - * - * e.g. - * git config --get-color color.diff.whitespace "blue reverse" - */ - const char *def_color = NULL; - - switch (argc) { - default: - usage(git_config_set_usage); - case 2: - def_color = argv[1]; - /* fallthru */ - case 1: - get_color_slot = argv[0]; - break; - } - get_color_found = 0; parsed_color[0] = '\0'; git_config(git_get_color_config, NULL); @@ -222,7 +265,6 @@ static int get_color(int argc, const char **argv) color_parse(def_color, "command line", parsed_color); fputs(parsed_color, stdout); - return 0; } static int stdout_is_tty; @@ -231,7 +273,7 @@ static int get_diff_color_found; static int git_get_colorbool_config(const char *var, const char *value, void *cb) { - if (!strcmp(var, get_color_slot)) { + if (!strcmp(var, get_colorbool_slot)) { get_colorbool_found = git_config_colorbool(var, value, stdout_is_tty); } @@ -246,191 +288,188 @@ static int git_get_colorbool_config(const char *var, const char *value, return 0; } -static int get_colorbool(int argc, const char **argv) +static int get_colorbool(int print) { - /* - * git config --get-colorbool <slot> [<stdout-is-tty>] - * - * returns "true" or "false" depending on how <slot> - * is configured. - */ - - if (argc == 2) - stdout_is_tty = git_config_bool("command line", argv[1]); - else if (argc == 1) - stdout_is_tty = isatty(1); - else - usage(git_config_set_usage); get_colorbool_found = -1; get_diff_color_found = -1; - get_color_slot = argv[0]; git_config(git_get_colorbool_config, NULL); if (get_colorbool_found < 0) { - if (!strcmp(get_color_slot, "color.diff")) + if (!strcmp(get_colorbool_slot, "color.diff")) get_colorbool_found = get_diff_color_found; if (get_colorbool_found < 0) get_colorbool_found = git_use_color_default; } - if (argc == 1) { - return get_colorbool_found ? 0 : 1; - } else { + if (print) { printf("%s\n", get_colorbool_found ? "true" : "false"); return 0; - } + } else + return get_colorbool_found ? 0 : 1; } -int cmd_config(int argc, const char **argv, const char *prefix) +int cmd_config(int argc, const char **argv, const char *unused_prefix) { int nongit; char *value; - const char *file = setup_git_directory_gently(&nongit); + const char *prefix = setup_git_directory_gently(&nongit); config_exclusive_filename = getenv(CONFIG_ENVIRONMENT); - while (1 < argc) { - if (!strcmp(argv[1], "--int")) - type = T_INT; - else if (!strcmp(argv[1], "--bool")) - type = T_BOOL; - else if (!strcmp(argv[1], "--bool-or-int")) - type = T_BOOL_OR_INT; - else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) { - if (argc != 2) - usage(git_config_set_usage); - if (git_config(show_all_config, NULL) < 0 && - file && errno) - die("unable to read config file %s: %s", file, - strerror(errno)); - return 0; - } - else if (!strcmp(argv[1], "--global")) { - char *home = getenv("HOME"); - if (home) { - char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); - config_exclusive_filename = user_config; - } else { - die("$HOME not set"); - } - } - else if (!strcmp(argv[1], "--system")) - config_exclusive_filename = git_etc_gitconfig(); - else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) { - if (argc < 3) - usage(git_config_set_usage); - if (!is_absolute_path(argv[2]) && file) - file = prefix_filename(file, strlen(file), - argv[2]); - else - file = argv[2]; - config_exclusive_filename = file; - argc--; - argv++; - } - else if (!strcmp(argv[1], "--null") || !strcmp(argv[1], "-z")) { - term = '\0'; - delim = '\n'; - key_delim = '\n'; - } - else if (!strcmp(argv[1], "--rename-section")) { - int ret; - if (argc != 4) - usage(git_config_set_usage); - ret = git_config_rename_section(argv[2], argv[3]); - if (ret < 0) - return ret; - if (ret == 0) { - fprintf(stderr, "No such section!\n"); - return 1; - } - return 0; - } - else if (!strcmp(argv[1], "--remove-section")) { - int ret; - if (argc != 3) - usage(git_config_set_usage); - ret = git_config_rename_section(argv[2], NULL); - if (ret < 0) - return ret; - if (ret == 0) { - fprintf(stderr, "No such section!\n"); - return 1; - } - return 0; - } else if (!strcmp(argv[1], "--get-color")) { - return get_color(argc-2, argv+2); - } else if (!strcmp(argv[1], "--get-colorbool")) { - return get_colorbool(argc-2, argv+2); - } else if (!strcmp(argv[1], "--edit") || !strcmp(argv[1], "-e")) { - if (argc != 2) - usage(git_config_set_usage); - git_config(git_default_config, NULL); - launch_editor(config_exclusive_filename ? - config_exclusive_filename : git_path("config"), - NULL, NULL); - return 0; - } else - break; - argc--; - argv++; - } - - switch (argc) { - case 2: - return get_value(argv[1], NULL); - case 3: - if (!strcmp(argv[1], "--unset")) - return git_config_set(argv[2], NULL); - else if (!strcmp(argv[1], "--unset-all")) - return git_config_set_multivar(argv[2], NULL, NULL, 1); - else if (!strcmp(argv[1], "--get")) - return get_value(argv[2], NULL); - else if (!strcmp(argv[1], "--get-all")) { - do_all = 1; - return get_value(argv[2], NULL); - } else if (!strcmp(argv[1], "--get-regexp")) { - show_keys = 1; - use_key_regexp = 1; - do_all = 1; - return get_value(argv[2], NULL); + argc = parse_options(argc, argv, builtin_config_options, builtin_config_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (use_global_config + use_system_config + !!given_config_file > 1) { + error("only one config file at a time."); + usage_with_options(builtin_config_usage, builtin_config_options); + } + + if (use_global_config) { + char *home = getenv("HOME"); + if (home) { + char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); + config_exclusive_filename = user_config; } else { - value = normalize_value(argv[1], argv[2]); - return git_config_set(argv[1], value); + die("$HOME not set"); } - case 4: - if (!strcmp(argv[1], "--unset")) - return git_config_set_multivar(argv[2], NULL, argv[3], 0); - else if (!strcmp(argv[1], "--unset-all")) - return git_config_set_multivar(argv[2], NULL, argv[3], 1); - else if (!strcmp(argv[1], "--get")) - return get_value(argv[2], argv[3]); - else if (!strcmp(argv[1], "--get-all")) { - do_all = 1; - return get_value(argv[2], argv[3]); - } else if (!strcmp(argv[1], "--get-regexp")) { - show_keys = 1; - use_key_regexp = 1; - do_all = 1; - return get_value(argv[2], argv[3]); - } else if (!strcmp(argv[1], "--add")) { - value = normalize_value(argv[2], argv[3]); - return git_config_set_multivar(argv[2], value, "^$", 0); - } else if (!strcmp(argv[1], "--replace-all")) { - value = normalize_value(argv[2], argv[3]); - return git_config_set_multivar(argv[2], value, NULL, 1); - } else { - value = normalize_value(argv[1], argv[2]); - return git_config_set_multivar(argv[1], value, argv[3], 0); + } + else if (use_system_config) + config_exclusive_filename = git_etc_gitconfig(); + else if (given_config_file) { + if (!is_absolute_path(given_config_file) && prefix) + config_exclusive_filename = prefix_filename(prefix, + strlen(prefix), + argv[2]); + else + config_exclusive_filename = given_config_file; + } + + if (end_null) { + term = '\0'; + delim = '\n'; + key_delim = '\n'; + } + + if (HAS_MULTI_BITS(types)) { + error("only one type at a time."); + usage_with_options(builtin_config_usage, builtin_config_options); + } + + if (get_color_slot) + actions |= ACTION_GET_COLOR; + if (get_colorbool_slot) + actions |= ACTION_GET_COLORBOOL; + + if ((get_color_slot || get_colorbool_slot) && types) { + error("--get-color and variable type are incoherent"); + usage_with_options(builtin_config_usage, builtin_config_options); + } + + if (HAS_MULTI_BITS(actions)) { + error("only one action at a time."); + usage_with_options(builtin_config_usage, builtin_config_options); + } + if (actions == 0) + switch (argc) { + case 1: actions = ACTION_GET; break; + case 2: actions = ACTION_SET; break; + case 3: actions = ACTION_SET_ALL; break; + default: + usage_with_options(builtin_config_usage, builtin_config_options); } - case 5: - if (!strcmp(argv[1], "--replace-all")) { - value = normalize_value(argv[2], argv[3]); - return git_config_set_multivar(argv[2], value, argv[4], 1); + + if (actions == ACTION_LIST) { + check_argc(argc, 0, 0); + if (git_config(show_all_config, NULL) < 0) { + if (config_exclusive_filename) + die("unable to read config file %s: %s", + config_exclusive_filename, strerror(errno)); + else + die("error processing config file(s)"); } - case 1: - default: - usage(git_config_set_usage); } + else if (actions == ACTION_EDIT) { + check_argc(argc, 0, 0); + git_config(git_default_config, NULL); + launch_editor(config_exclusive_filename ? + config_exclusive_filename : git_path("config"), + NULL, NULL); + } + else if (actions == ACTION_SET) { + check_argc(argc, 2, 2); + value = normalize_value(argv[0], argv[1]); + return git_config_set(argv[0], value); + } + else if (actions == ACTION_SET_ALL) { + check_argc(argc, 2, 3); + value = normalize_value(argv[0], argv[1]); + return git_config_set_multivar(argv[0], value, argv[2], 0); + } + else if (actions == ACTION_ADD) { + check_argc(argc, 2, 2); + value = normalize_value(argv[0], argv[1]); + return git_config_set_multivar(argv[0], value, "^$", 0); + } + else if (actions == ACTION_REPLACE_ALL) { + check_argc(argc, 2, 3); + value = normalize_value(argv[0], argv[1]); + return git_config_set_multivar(argv[0], value, argv[2], 1); + } + else if (actions == ACTION_GET) { + check_argc(argc, 1, 2); + return get_value(argv[0], argv[1]); + } + else if (actions == ACTION_GET_ALL) { + do_all = 1; + check_argc(argc, 1, 2); + return get_value(argv[0], argv[1]); + } + else if (actions == ACTION_GET_REGEXP) { + show_keys = 1; + use_key_regexp = 1; + do_all = 1; + check_argc(argc, 1, 2); + return get_value(argv[0], argv[1]); + } + else if (actions == ACTION_UNSET) { + check_argc(argc, 1, 2); + if (argc == 2) + return git_config_set_multivar(argv[0], NULL, argv[1], 0); + else + return git_config_set(argv[0], NULL); + } + else if (actions == ACTION_UNSET_ALL) { + check_argc(argc, 1, 2); + return git_config_set_multivar(argv[0], NULL, argv[1], 1); + } + else if (actions == ACTION_RENAME_SECTION) { + int ret; + check_argc(argc, 2, 2); + ret = git_config_rename_section(argv[0], argv[1]); + if (ret < 0) + return ret; + if (ret == 0) + die("No such section!"); + } + else if (actions == ACTION_REMOVE_SECTION) { + int ret; + check_argc(argc, 1, 1); + ret = git_config_rename_section(argv[0], NULL); + if (ret < 0) + return ret; + if (ret == 0) + die("No such section!"); + } + else if (actions == ACTION_GET_COLOR) { + get_color(argv[0]); + } + else if (actions == ACTION_GET_COLORBOOL) { + if (argc == 1) + stdout_is_tty = git_config_bool("command line", argv[0]); + else if (argc == 0) + stdout_is_tty = isatty(1); + return get_colorbool(argc != 0); + } + return 0; } diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c index 8ecefd4f0..79cedb72c 100644 --- a/builtin-diff-tree.c +++ b/builtin-diff-tree.c @@ -102,7 +102,6 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) init_revisions(opt, prefix); git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ - nr_sha1 = 0; opt->abbrev = 0; opt->diff = 1; argc = setup_revisions(argc, argv, opt, NULL); diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index c2e5adc88..d571253a5 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -482,7 +482,7 @@ static int sideband_demux(int fd, void *data) { int *xd = data; - return recv_sideband("fetch-pack", xd[0], fd, 2); + return recv_sideband("fetch-pack", xd[0], fd); } static int get_pack(int xd[2], char **pack_lockfile) @@ -800,15 +800,13 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args, int fd; mtime.sec = st.st_mtime; -#ifdef USE_NSEC - mtime.usec = st.st_mtim.usec; -#endif + mtime.nsec = ST_MTIME_NSEC(st); if (stat(shallow, &st)) { if (mtime.sec) die("shallow file was removed during fetch"); } else if (st.st_mtime != mtime.sec #ifdef USE_NSEC - || st.st_mtim.usec != mtime.usec + || ST_MTIME_NSEC(st) != mtime.nsec #endif ) die("shallow file was changed during fetch"); diff --git a/builtin-fetch.c b/builtin-fetch.c index 1e4a3d9c5..7fb35fca9 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -636,6 +636,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) else remote = remote_get(argv[0]); + if (!remote) + die("Where do you want to fetch from today?"); + transport = transport_get(remote, remote->url[0]); if (verbosity >= 2) transport->verbose = 1; @@ -648,9 +651,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) if (depth) set_option(TRANS_OPT_DEPTH, depth); - if (!transport->url) - die("Where do you want to fetch from today?"); - if (argc > 1) { int j = 0; refs = xcalloc(argc + 1, sizeof(const char *)); diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index e46b7adc9..5cbb4b081 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -943,7 +943,6 @@ static int opt_parse_sort(const struct option *opt, const char *arg, int unset) return -1; *sort_tail = s = xcalloc(1, sizeof(*s)); - sort_tail = &s->next; if (*arg == '-') { s->reverse = 1; diff --git a/builtin-gc.c b/builtin-gc.c index 8d990ed49..fc556ed7f 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -23,7 +23,7 @@ static const char * const builtin_gc_usage[] = { }; static int pack_refs = 1; -static int aggressive_window = -1; +static int aggressive_window = 250; static int gc_auto_threshold = 6700; static int gc_auto_pack_limit = 50; static const char *prune_expire = "2.weeks.ago"; @@ -200,6 +200,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (aggressive) { append_option(argv_repack, "-f", MAX_ADD); + append_option(argv_repack, "--depth=250", MAX_ADD); if (aggressive_window > 0) { sprintf(buf, "--window=%d", aggressive_window); append_option(argv_repack, buf, MAX_ADD); diff --git a/builtin-grep.c b/builtin-grep.c index 3f12ba382..89489ddcf 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -22,6 +22,28 @@ static int builtin_grep; +static int grep_config(const char *var, const char *value, void *cb) +{ + struct grep_opt *opt = cb; + + if (!strcmp(var, "grep.color") || !strcmp(var, "color.grep")) { + opt->color = git_config_colorbool(var, value, -1); + return 0; + } + if (!strcmp(var, "grep.color.external") || + !strcmp(var, "color.grep.external")) { + return git_config_string(&(opt->color_external), var, value); + } + if (!strcmp(var, "grep.color.match") || + !strcmp(var, "color.grep.match")) { + if (!value) + return config_error_nonbool(var); + color_parse(value, var, opt->color_match); + return 0; + } + return git_color_default_config(var, value, cb); +} + /* * git grep pathspecs are somewhat different from diff-tree pathspecs; * pathname wildcards are allowed. @@ -269,6 +291,21 @@ static int flush_grep(struct grep_opt *opt, return status; } +static void grep_add_color(struct strbuf *sb, const char *escape_seq) +{ + size_t orig_len = sb->len; + + while (*escape_seq) { + if (*escape_seq == 'm') + strbuf_addch(sb, ';'); + else if (*escape_seq != '\033' && *escape_seq != '[') + strbuf_addch(sb, *escape_seq); + escape_seq++; + } + if (sb->len > orig_len && sb->buf[sb->len - 1] == ';') + strbuf_setlen(sb, sb->len - 1); +} + static int external_grep(struct grep_opt *opt, const char **paths, int cached) { int i, nr, argc, hit, len, status; @@ -339,6 +376,23 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached) push_arg("-e"); push_arg(p->pattern); } + if (opt->color) { + struct strbuf sb = STRBUF_INIT; + + grep_add_color(&sb, opt->color_match); + setenv("GREP_COLOR", sb.buf, 1); + + strbuf_reset(&sb); + strbuf_addstr(&sb, "mt="); + grep_add_color(&sb, opt->color_match); + strbuf_addstr(&sb, ":sl=:cx=:fn=:ln=:bn=:se="); + setenv("GREP_COLORS", sb.buf, 1); + + strbuf_release(&sb); + + if (opt->color_external && strlen(opt->color_external) > 0) + push_arg(opt->color_external); + } hit = 0; argc = nr; @@ -536,6 +590,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix) opt.pattern_tail = &opt.pattern_list; opt.regflags = REG_NEWLINE; + strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD); + opt.color = -1; + git_config(grep_config, &opt); + if (opt.color == -1) + opt.color = git_use_color_default; + /* * If there is no -- then the paths must exist in the working * tree. If there is no explicit pattern specified with -e or @@ -732,6 +792,14 @@ int cmd_grep(int argc, const char **argv, const char *prefix) 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--; @@ -757,6 +825,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } } + if (opt.color && !opt.color_external) + builtin_grep = 1; if (!opt.pattern_list) die("no pattern given."); if ((opt.regflags != REG_NEWLINE) && opt.fixed) diff --git a/builtin-log.c b/builtin-log.c index 8684fcdb6..8af55d287 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -573,7 +573,7 @@ static FILE *realstdout = NULL; static const char *output_directory = NULL; static int outdir_offset; -static int reopen_stdout(const char *oneline, int nr, int total) +static int reopen_stdout(const char *oneline, int nr, struct rev_info *rev) { char filename[PATH_MAX]; int len = 0; @@ -598,7 +598,9 @@ static int reopen_stdout(const char *oneline, int nr, int total) strcpy(filename + len, fmt_patch_suffix); } - fprintf(realstdout, "%s\n", filename + outdir_offset); + if (!DIFF_OPT_TST(&rev->diffopt, QUIET)) + fprintf(realstdout, "%s\n", filename + outdir_offset); + if (freopen(filename, "w", stdout) == NULL) return error("Cannot open patch file %s",filename); @@ -687,7 +689,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, die("Cover letter needs email format"); if (!use_stdout && reopen_stdout(numbered_files ? - NULL : "cover-letter", 0, rev->total)) + NULL : "cover-letter", 0, rev)) return; head_sha1 = sha1_to_hex(head->object.sha1); @@ -1106,7 +1108,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) } if (!use_stdout && reopen_stdout(numbered_files ? NULL : get_oneline_for_filename(commit, keep_subject), - rev.nr, rev.total)) + rev.nr, &rev)) die("Failed to create output files"); shown = log_tree_commit(&rev, commit); free(commit->buffer); diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 9dec282fb..ca6f33d04 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -419,6 +419,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix) } if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) { show_deleted = 1; + require_work_tree = 1; continue; } if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) { diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c index fca46312f..22008dfa8 100644 --- a/builtin-ls-tree.c +++ b/builtin-ls-tree.c @@ -60,7 +60,6 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen, { int retval = 0; const char *type = blob_type; - unsigned long size; if (S_ISGITLINK(mode)) { /* @@ -90,17 +89,20 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen, if (!(ls_options & LS_NAME_ONLY)) { if (ls_options & LS_SHOW_SIZE) { + char size_text[24]; if (!strcmp(type, blob_type)) { - sha1_object_info(sha1, &size); - printf("%06o %s %s %7lu\t", mode, type, - abbrev ? find_unique_abbrev(sha1, abbrev) - : sha1_to_hex(sha1), - size); + unsigned long size; + if (sha1_object_info(sha1, &size) == OBJ_BAD) + strcpy(size_text, "BAD"); + else + snprintf(size_text, sizeof(size_text), + "%lu", size); } else - printf("%06o %s %s %7c\t", mode, type, - abbrev ? find_unique_abbrev(sha1, abbrev) - : sha1_to_hex(sha1), - '-'); + strcpy(size_text, "-"); + printf("%06o %s %s %7s\t", mode, type, + abbrev ? find_unique_abbrev(sha1, abbrev) + : sha1_to_hex(sha1), + size_text); } else printf("%06o %s %s\t", mode, type, abbrev ? find_unique_abbrev(sha1, abbrev) diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index 2789ccdf7..1eeeb4de6 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -537,7 +537,6 @@ static int decode_header_bq(struct strbuf *it) */ strbuf_add(&outbuf, in, ep - in); } - in = ep; } /* E.g. * ep : "=?iso-2022-jp?B?GyR...?= foo" diff --git a/builtin-push.c b/builtin-push.c index 122fdcfbd..ca36fb1e5 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -53,8 +53,11 @@ static int do_push(const char *repo, int flags) int i, errs; struct remote *remote = remote_get(repo); - if (!remote) - die("bad repository '%s'", repo); + if (!remote) { + if (repo) + die("bad repository '%s'", repo); + die("No destination configured to push to."); + } if (remote->mirror) flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE); diff --git a/builtin-remote.c b/builtin-remote.c index e171096ec..993acd6a0 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -12,12 +12,17 @@ static const char * const builtin_remote_usage[] = { "git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>", "git remote rename <old> <new>", "git remote rm <name>", + "git remote set-head <name> [-a | -d | <branch>]", "git remote show [-n] <name>", "git remote prune [-n | --dry-run] <name>", "git remote [-v | --verbose] update [group]", NULL }; +#define GET_REF_STATES (1<<0) +#define GET_HEAD_NAMES (1<<1) +#define GET_PUSH_REF_STATES (1<<2) + static int verbose; static int show_all(void); @@ -143,8 +148,9 @@ static int add(int argc, const char **argv) } struct branch_info { - char *remote; + char *remote_name; struct string_list merge; + int rebase; }; static struct string_list branch_list; @@ -161,10 +167,11 @@ static const char *abbrev_ref(const char *name, const char *prefix) static int config_read_branches(const char *key, const char *value, void *cb) { if (!prefixcmp(key, "branch.")) { + const char *orig_key = key; char *name; struct string_list_item *item; struct branch_info *info; - enum { REMOTE, MERGE } type; + enum { REMOTE, MERGE, REBASE } type; key += 7; if (!postfixcmp(key, ".remote")) { @@ -173,6 +180,9 @@ static int config_read_branches(const char *key, const char *value, void *cb) } else if (!postfixcmp(key, ".merge")) { name = xstrndup(key, strlen(key) - 6); type = MERGE; + } else if (!postfixcmp(key, ".rebase")) { + name = xstrndup(key, strlen(key) - 7); + type = REBASE; } else return 0; @@ -182,10 +192,10 @@ static int config_read_branches(const char *key, const char *value, void *cb) item->util = xcalloc(sizeof(struct branch_info), 1); info = item->util; if (type == REMOTE) { - if (info->remote) - warning("more than one branch.%s", key); - info->remote = xstrdup(value); - } else { + if (info->remote_name) + warning("more than one %s", orig_key); + info->remote_name = xstrdup(value); + } else if (type == MERGE) { char *space = strchr(value, ' '); value = abbrev_branch(value); while (space) { @@ -196,7 +206,8 @@ static int config_read_branches(const char *key, const char *value, void *cb) space = strchr(value, ' '); } string_list_append(xstrdup(value), &info->merge); - } + } else + info->rebase = git_config_bool(orig_key, value); } return 0; } @@ -206,12 +217,12 @@ static void read_branches(void) if (branch_list.nr) return; git_config(config_read_branches, NULL); - sort_string_list(&branch_list); } struct ref_states { struct remote *remote; - struct string_list new, stale, tracked; + struct string_list new, stale, tracked, heads, push; + int queried; }; static int handle_one_branch(const char *refname, @@ -227,10 +238,8 @@ static int handle_one_branch(const char *refname, const char *name = abbrev_branch(refspec.src); /* symbolic refs pointing nowhere were handled already */ if ((flags & REF_ISSYMREF) || - unsorted_string_list_has_string(&states->tracked, - name) || - unsorted_string_list_has_string(&states->new, - name)) + string_list_has_string(&states->tracked, name) || + string_list_has_string(&states->new, name)) return 0; item = string_list_append(name, &states->stale); item->util = xstrdup(refname); @@ -238,39 +247,154 @@ static int handle_one_branch(const char *refname, return 0; } -static int get_ref_states(const struct ref *ref, struct ref_states *states) +static int get_ref_states(const struct ref *remote_refs, struct ref_states *states) { struct ref *fetch_map = NULL, **tail = &fetch_map; + struct ref *ref; int i; for (i = 0; i < states->remote->fetch_refspec_nr; i++) - if (get_fetch_map(ref, states->remote->fetch + i, &tail, 1)) + if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1)) die("Could not get fetch map for refspec %s", states->remote->fetch_refspec[i]); states->new.strdup_strings = states->tracked.strdup_strings = 1; for (ref = fetch_map; ref; ref = ref->next) { - struct string_list *target = &states->tracked; unsigned char sha1[20]; - void *util = NULL; - if (!ref->peer_ref || read_ref(ref->peer_ref->name, sha1)) - target = &states->new; - else { - target = &states->tracked; - if (hashcmp(sha1, ref->new_sha1)) - util = &states; - } - string_list_append(abbrev_branch(ref->name), target)->util = util; + string_list_append(abbrev_branch(ref->name), &states->new); + else + string_list_append(abbrev_branch(ref->name), &states->tracked); } free_refs(fetch_map); + sort_string_list(&states->new); + sort_string_list(&states->tracked); for_each_ref(handle_one_branch, states); sort_string_list(&states->stale); return 0; } +struct push_info { + char *dest; + int forced; + enum { + PUSH_STATUS_CREATE = 0, + PUSH_STATUS_DELETE, + PUSH_STATUS_UPTODATE, + PUSH_STATUS_FASTFORWARD, + PUSH_STATUS_OUTOFDATE, + PUSH_STATUS_NOTQUERIED, + } status; +}; + +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; + if (remote->mirror) + return 0; + + local_refs = get_local_heads(); + ref = push_map = copy_ref_list(remote_refs); + while (ref->next) + ref = ref->next; + push_tail = &ref->next; + + match_refs(local_refs, push_map, &push_tail, remote->push_refspec_nr, + remote->push_refspec, MATCH_REFS_NONE); + + states->push.strdup_strings = 1; + for (ref = push_map; ref; ref = ref->next) { + struct string_list_item *item; + struct push_info *info; + + if (!ref->peer_ref) + continue; + hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); + + item = string_list_append(abbrev_branch(ref->peer_ref->name), + &states->push); + item->util = xcalloc(sizeof(struct push_info), 1); + info = item->util; + info->forced = ref->force; + info->dest = xstrdup(abbrev_branch(ref->name)); + + if (is_null_sha1(ref->new_sha1)) { + info->status = PUSH_STATUS_DELETE; + } else if (!hashcmp(ref->old_sha1, ref->new_sha1)) + info->status = PUSH_STATUS_UPTODATE; + else if (is_null_sha1(ref->old_sha1)) + info->status = PUSH_STATUS_CREATE; + else if (has_sha1_file(ref->old_sha1) && + ref_newer(ref->new_sha1, ref->old_sha1)) + info->status = PUSH_STATUS_FASTFORWARD; + else + info->status = PUSH_STATUS_OUTOFDATE; + } + free_refs(local_refs); + free_refs(push_map); + return 0; +} + +static int get_push_ref_states_noquery(struct ref_states *states) +{ + int i; + struct remote *remote = states->remote; + struct string_list_item *item; + struct push_info *info; + + if (remote->mirror) + return 0; + + states->push.strdup_strings = 1; + if (!remote->push_refspec_nr) { + item = string_list_append("(matching)", &states->push); + info = item->util = xcalloc(sizeof(struct push_info), 1); + info->status = PUSH_STATUS_NOTQUERIED; + info->dest = xstrdup(item->string); + } + for (i = 0; i < remote->push_refspec_nr; i++) { + struct refspec *spec = remote->push + i; + if (spec->matching) + item = string_list_append("(matching)", &states->push); + else if (strlen(spec->src)) + item = string_list_append(spec->src, &states->push); + else + item = string_list_append("(delete)", &states->push); + + info = item->util = xcalloc(sizeof(struct push_info), 1); + info->forced = spec->force; + info->status = PUSH_STATUS_NOTQUERIED; + info->dest = xstrdup(spec->dst ? spec->dst : item->string); + } + return 0; +} + +static int get_head_names(const struct ref *remote_refs, struct ref_states *states) +{ + struct ref *ref, *matches; + struct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map; + struct refspec refspec; + + refspec.force = 0; + refspec.pattern = 1; + refspec.src = refspec.dst = "refs/heads/*"; + states->heads.strdup_strings = 1; + get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0); + matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"), + fetch_map, 1); + for(ref = matches; ref; ref = ref->next) + string_list_append(abbrev_branch(ref->name), &states->heads); + + free_refs(fetch_map); + free_refs(matches); + + return 0; +} + struct known_remote { struct known_remote *next; struct remote *remote; @@ -466,7 +590,7 @@ static int mv(int argc, const char **argv) for (i = 0; i < branch_list.nr; i++) { struct string_list_item *item = branch_list.items + i; struct branch_info *info = item->util; - if (info->remote && !strcmp(info->remote, rename.old)) { + if (info->remote_name && !strcmp(info->remote_name, rename.old)) { strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.remote", item->string); if (git_config_set(buf.buf, rename.new)) { @@ -575,7 +699,7 @@ static int rm(int argc, const char **argv) for (i = 0; i < branch_list.nr; i++) { struct string_list_item *item = branch_list.items + i; struct branch_info *info = item->util; - if (info->remote && !strcmp(info->remote, remote->name)) { + if (info->remote_name && !strcmp(info->remote_name, remote->name)) { const char *keys[] = { "remote", "merge", NULL }, **k; for (k = keys; *k; k++) { strbuf_reset(&buf); @@ -617,18 +741,37 @@ static int rm(int argc, const char **argv) return result; } -static void show_list(const char *title, struct string_list *list, - const char *extra_arg) +void clear_push_info(void *util, const char *string) { - int i; + struct push_info *info = util; + free(info->dest); + free(info); +} - if (!list->nr) - return; +static void free_remote_ref_states(struct ref_states *states) +{ + string_list_clear(&states->new, 0); + string_list_clear(&states->stale, 0); + string_list_clear(&states->tracked, 0); + string_list_clear(&states->heads, 0); + string_list_clear_func(&states->push, clear_push_info); +} - printf(title, list->nr > 1 ? "es" : "", extra_arg); - printf("\n"); - for (i = 0; i < list->nr; i++) - printf(" %s\n", list->items[i].string); +static int append_ref_to_tracked_list(const char *refname, + const unsigned char *sha1, int flags, void *cb_data) +{ + struct ref_states *states = cb_data; + struct refspec refspec; + + if (flags & REF_ISSYMREF) + return 0; + + memset(&refspec, 0, sizeof(refspec)); + refspec.dst = (char *)refname; + if (!remote_find_tracking(states->remote, &refspec)) + string_list_append(abbrev_branch(refspec.src), &states->tracked); + + return 0; } static int get_remote_ref_states(const char *name, @@ -636,7 +779,7 @@ static int get_remote_ref_states(const char *name, int query) { struct transport *transport; - const struct ref *ref; + const struct ref *remote_refs; states->remote = remote_get(name); if (!states->remote) @@ -647,102 +790,318 @@ static int get_remote_ref_states(const char *name, if (query) { transport = transport_get(NULL, states->remote->url_nr > 0 ? states->remote->url[0] : NULL); - ref = transport_get_remote_refs(transport); + remote_refs = transport_get_remote_refs(transport); transport_disconnect(transport); - get_ref_states(ref, states); + states->queried = 1; + if (query & GET_REF_STATES) + get_ref_states(remote_refs, states); + if (query & GET_HEAD_NAMES) + get_head_names(remote_refs, states); + if (query & GET_PUSH_REF_STATES) + get_push_ref_states(remote_refs, states); + } else { + for_each_ref(append_ref_to_tracked_list, states); + sort_string_list(&states->tracked); + get_push_ref_states_noquery(states); } return 0; } -static int append_ref_to_tracked_list(const char *refname, - const unsigned char *sha1, int flags, void *cb_data) +struct show_info { + struct string_list *list; + struct ref_states *states; + int width, width2; + int any_rebase; +}; + +int add_remote_to_show_info(struct string_list_item *item, void *cb_data) { - struct ref_states *states = cb_data; - struct refspec refspec; + struct show_info *info = cb_data; + int n = strlen(item->string); + if (n > info->width) + info->width = n; + string_list_insert(item->string, info->list); + return 0; +} - memset(&refspec, 0, sizeof(refspec)); - refspec.dst = (char *)refname; - if (!remote_find_tracking(states->remote, &refspec)) - string_list_append(abbrev_branch(refspec.src), &states->tracked); +int show_remote_info_item(struct string_list_item *item, void *cb_data) +{ + struct show_info *info = cb_data; + struct ref_states *states = info->states; + const char *name = item->string; + + if (states->queried) { + const char *fmt = "%s"; + const char *arg = ""; + if (string_list_has_string(&states->new, name)) { + fmt = " new (next fetch will store in remotes/%s)"; + arg = states->remote->name; + } else if (string_list_has_string(&states->tracked, name)) + arg = " tracked"; + else if (string_list_has_string(&states->stale, name)) + arg = " stale (use 'git remote prune' to remove)"; + else + arg = " ???"; + printf(" %-*s", info->width, name); + printf(fmt, arg); + printf("\n"); + } else + printf(" %s\n", name); + + return 0; +} + +int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data) +{ + struct show_info *show_info = cb_data; + struct ref_states *states = show_info->states; + struct branch_info *branch_info = branch_item->util; + struct string_list_item *item; + int n; + + if (!branch_info->merge.nr || !branch_info->remote_name || + strcmp(states->remote->name, branch_info->remote_name)) + return 0; + if ((n = strlen(branch_item->string)) > show_info->width) + show_info->width = n; + if (branch_info->rebase) + show_info->any_rebase = 1; + + item = string_list_insert(branch_item->string, show_info->list); + item->util = branch_info; + + return 0; +} + +int show_local_info_item(struct string_list_item *item, void *cb_data) +{ + struct show_info *show_info = cb_data; + struct branch_info *branch_info = item->util; + struct string_list *merge = &branch_info->merge; + const char *also; + int i; + + if (branch_info->rebase && branch_info->merge.nr > 1) { + error("invalid branch.%s.merge; cannot rebase onto > 1 branch", + item->string); + return 0; + } + + printf(" %-*s ", show_info->width, item->string); + if (branch_info->rebase) { + printf("rebases onto remote %s\n", merge->items[0].string); + return 0; + } else if (show_info->any_rebase) { + printf(" merges with remote %s\n", merge->items[0].string); + also = " and with remote"; + } else { + printf("merges with remote %s\n", merge->items[0].string); + also = " and with remote"; + } + for (i = 1; i < merge->nr; i++) + printf(" %-*s %s %s\n", show_info->width, "", also, + merge->items[i].string); + + return 0; +} +int add_push_to_show_info(struct string_list_item *push_item, void *cb_data) +{ + struct show_info *show_info = cb_data; + struct push_info *push_info = push_item->util; + struct string_list_item *item; + int n; + if ((n = strlen(push_item->string)) > show_info->width) + show_info->width = n; + if ((n = strlen(push_info->dest)) > show_info->width2) + show_info->width2 = n; + item = string_list_append(push_item->string, show_info->list); + item->util = push_item->util; + return 0; +} + +int show_push_info_item(struct string_list_item *item, void *cb_data) +{ + struct show_info *show_info = cb_data; + struct push_info *push_info = item->util; + char *src = item->string, *status = NULL; + + switch (push_info->status) { + case PUSH_STATUS_CREATE: + status = "create"; + break; + case PUSH_STATUS_DELETE: + status = "delete"; + src = "(none)"; + break; + case PUSH_STATUS_UPTODATE: + status = "up to date"; + break; + case PUSH_STATUS_FASTFORWARD: + status = "fast forwardable"; + break; + case PUSH_STATUS_OUTOFDATE: + status = "local out of date"; + break; + case PUSH_STATUS_NOTQUERIED: + break; + } + if (status) + printf(" %-*s %s to %-*s (%s)\n", show_info->width, src, + push_info->forced ? "forces" : "pushes", + show_info->width2, push_info->dest, status); + else + printf(" %-*s %s to %s\n", show_info->width, src, + push_info->forced ? "forces" : "pushes", + push_info->dest); return 0; } static int show(int argc, const char **argv) { - int no_query = 0, result = 0; + int no_query = 0, result = 0, query_flag = 0; struct option options[] = { OPT_GROUP("show specific options"), OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"), OPT_END() }; struct ref_states states; + struct string_list info_list = { NULL, 0, 0, 0 }; + struct show_info info; argc = parse_options(argc, argv, options, builtin_remote_usage, 0); if (argc < 1) return show_all(); + if (!no_query) + query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES); + memset(&states, 0, sizeof(states)); + memset(&info, 0, sizeof(info)); + info.states = &states; + info.list = &info_list; for (; argc; argc--, argv++) { int i; - get_remote_ref_states(*argv, &states, !no_query); + 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)"); - - for (i = 0; i < branch_list.nr; i++) { - struct string_list_item *branch = branch_list.items + i; - struct branch_info *info = branch->util; - int j; - - if (!info->merge.nr || strcmp(*argv, info->remote)) - continue; - printf(" Remote branch%s merged with 'git pull' " - "while on branch %s\n ", - info->merge.nr > 1 ? "es" : "", - branch->string); - for (j = 0; j < info->merge.nr; j++) - printf(" %s", info->merge.items[j].string); - printf("\n"); + if (no_query) + printf(" HEAD branch: (not queried)\n"); + else if (!states.heads.nr) + printf(" HEAD branch: (unknown)\n"); + else if (states.heads.nr == 1) + printf(" HEAD branch: %s\n", states.heads.items[0].string); + else { + printf(" HEAD branch (remote HEAD is ambiguous," + " may be one of the following):\n"); + for (i = 0; i < states.heads.nr; i++) + printf(" %s\n", states.heads.items[i].string); } - if (!no_query) { - show_list(" New remote branch%s (next fetch " - "will store in remotes/%s)", - &states.new, states.remote->name); - show_list(" Stale tracking branch%s (use 'git remote " - "prune')", &states.stale, ""); - } + /* remote branch info */ + info.width = 0; + for_each_string_list(add_remote_to_show_info, &states.new, &info); + for_each_string_list(add_remote_to_show_info, &states.tracked, &info); + for_each_string_list(add_remote_to_show_info, &states.stale, &info); + if (info.list->nr) + printf(" Remote branch%s:%s\n", + info.list->nr > 1 ? "es" : "", + no_query ? " (status not queried)" : ""); + for_each_string_list(show_remote_info_item, info.list, &info); + string_list_clear(info.list, 0); + + /* git pull info */ + info.width = 0; + info.any_rebase = 0; + for_each_string_list(add_local_to_show_info, &branch_list, &info); + if (info.list->nr) + printf(" Local branch%s configured for 'git pull':\n", + info.list->nr > 1 ? "es" : ""); + for_each_string_list(show_local_info_item, info.list, &info); + string_list_clear(info.list, 0); + + /* git push info */ + if (states.remote->mirror) + printf(" Local refs will be mirrored by 'git push'\n"); + + info.width = info.width2 = 0; + for_each_string_list(add_push_to_show_info, &states.push, &info); + sort_string_list(info.list); + if (info.list->nr) + printf(" Local ref%s configured for 'git push'%s:\n", + info.list->nr > 1 ? "s" : "", + no_query ? " (status not queried)" : ""); + for_each_string_list(show_push_info_item, info.list, &info); + string_list_clear(info.list, 0); + + free_remote_ref_states(&states); + } - if (no_query) - for_each_ref(append_ref_to_tracked_list, &states); - show_list(" Tracked remote branch%s", &states.tracked, ""); - - if (states.remote->push_refspec_nr) { - printf(" Local branch%s pushed with 'git push'\n", - states.remote->push_refspec_nr > 1 ? - "es" : ""); - for (i = 0; i < states.remote->push_refspec_nr; i++) { - struct refspec *spec = states.remote->push + i; - printf(" %s%s%s%s\n", - spec->force ? "+" : "", - abbrev_branch(spec->src), - spec->dst ? ":" : "", - spec->dst ? abbrev_branch(spec->dst) : ""); - } - } + return result; +} - /* NEEDSWORK: free remote */ - string_list_clear(&states.new, 0); - string_list_clear(&states.stale, 0); - string_list_clear(&states.tracked, 0); +static int set_head(int argc, const char **argv) +{ + int i, opt_a = 0, opt_d = 0, result = 0; + struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT; + char *head_name = NULL; + + struct option options[] = { + OPT_GROUP("set-head specific options"), + OPT_BOOLEAN('a', "auto", &opt_a, + "set refs/remotes/<name>/HEAD according to remote"), + OPT_BOOLEAN('d', "delete", &opt_d, + "delete refs/remotes/<name>/HEAD"), + OPT_END() + }; + argc = parse_options(argc, argv, options, builtin_remote_usage, 0); + if (argc) + strbuf_addf(&buf, "refs/remotes/%s/HEAD", argv[0]); + + if (!opt_a && !opt_d && argc == 2) { + head_name = xstrdup(argv[1]); + } else if (opt_a && !opt_d && argc == 1) { + struct ref_states states; + memset(&states, 0, sizeof(states)); + get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES); + if (!states.heads.nr) + result |= error("Cannot determine remote HEAD"); + else if (states.heads.nr > 1) { + result |= error("Multiple remote HEAD branches. " + "Please choose one explicitly with:"); + for (i = 0; i < states.heads.nr; i++) + fprintf(stderr, " git remote set-head %s %s\n", + argv[0], states.heads.items[i].string); + } else + head_name = xstrdup(states.heads.items[0].string); + free_remote_ref_states(&states); + } else if (opt_d && !opt_a && argc == 1) { + if (delete_ref(buf.buf, NULL, REF_NODEREF)) + result |= error("Could not delete %s", buf.buf); + } else + usage_with_options(builtin_remote_usage, options); + + if (head_name) { + unsigned char sha1[20]; + strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name); + /* make sure it's valid */ + if (!resolve_ref(buf2.buf, sha1, 1, NULL)) + result |= error("Not a valid ref: %s", buf2.buf); + else if (create_symref(buf.buf, buf2.buf, "remote set-head")) + result |= error("Could not setup %s", buf.buf); + if (opt_a) + printf("%s/HEAD set to %s\n", argv[0], head_name); + free(head_name); } + strbuf_release(&buf); + strbuf_release(&buf2); return result; } @@ -770,7 +1129,7 @@ static int prune(int argc, const char **argv) for (; argc; argc--, argv++) { int i; - get_remote_ref_states(*argv, &states, 1); + get_remote_ref_states(*argv, &states, GET_REF_STATES); if (states.stale.nr) { printf("Pruning %s\n", *argv); @@ -791,10 +1150,7 @@ static int prune(int argc, const char **argv) warn_dangling_symref(dangling_msg, refname); } - /* NEEDSWORK: free remote */ - string_list_clear(&states.new, 0); - string_list_clear(&states.stale, 0); - string_list_clear(&states.tracked, 0); + free_remote_ref_states(&states); } return result; @@ -919,6 +1275,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix) result = mv(argc, argv); else if (!strcmp(argv[0], "rm")) result = rm(argc, argv); + else if (!strcmp(argv[0], "set-head")) + result = set_head(argc, argv); else if (!strcmp(argv[0], "show")) result = show(argc, argv); else if (!strcmp(argv[0], "prune")) diff --git a/builtin-send-pack.c b/builtin-send-pack.c index d65d01969..9072905f1 100644 --- a/builtin-send-pack.c +++ b/builtin-send-pack.c @@ -1,6 +1,5 @@ #include "cache.h" #include "commit.h" -#include "tag.h" #include "refs.h" #include "pkt-line.h" #include "run-command.h" @@ -84,82 +83,8 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext return 0; } -static void unmark_and_free(struct commit_list *list, unsigned int mark) -{ - while (list) { - struct commit_list *temp = list; - temp->item->object.flags &= ~mark; - list = temp->next; - free(temp); - } -} - -static int ref_newer(const unsigned char *new_sha1, - const unsigned char *old_sha1) -{ - struct object *o; - struct commit *old, *new; - struct commit_list *list, *used; - int found = 0; - - /* Both new and old must be commit-ish and new is descendant of - * old. Otherwise we require --force. - */ - o = deref_tag(parse_object(old_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - old = (struct commit *) o; - - o = deref_tag(parse_object(new_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - new = (struct commit *) o; - - if (parse_commit(new) < 0) - return 0; - - used = list = NULL; - commit_list_insert(new, &list); - while (list) { - new = pop_most_recent_commit(&list, 1); - commit_list_insert(new, &used); - if (new == old) { - found = 1; - break; - } - } - unmark_and_free(list, 1); - unmark_and_free(used, 1); - return found; -} - -static struct ref *local_refs, **local_tail; static struct ref *remote_refs, **remote_tail; -static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) -{ - struct ref *ref; - int len; - - /* we already know it starts with refs/ to get here */ - if (check_ref_format(refname + 5)) - return 0; - - len = strlen(refname) + 1; - ref = xcalloc(1, sizeof(*ref) + len); - hashcpy(ref->new_sha1, sha1); - memcpy(ref->name, refname, len); - *local_tail = ref; - local_tail = &ref->next; - return 0; -} - -static void get_local_heads(void) -{ - local_tail = &local_refs; - for_each_ref(one_local_ref, NULL); -} - static int receive_status(int in, struct ref *refs) { struct ref *hint; @@ -387,7 +312,7 @@ static int refs_pushed(struct ref *ref) static int do_send_pack(int in, int out, struct remote *remote, const char *dest, int nr_refspec, const char **refspec) { - struct ref *ref; + struct ref *ref, *local_refs; int new_refs; int ask_for_status_report = 0; int allow_deleting_refs = 0; @@ -405,7 +330,7 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest /* No funny business with the matcher */ remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL, &extra_have); - get_local_heads(); + local_refs = get_local_heads(); /* Does the other end support the reporting? */ if (server_supports("report-status")) diff --git a/builtin-shortlog.c b/builtin-shortlog.c index badd91203..b28091b44 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -101,7 +101,6 @@ static void insert_one_record(struct shortlog *log, } while (*oneline && isspace(*oneline) && *oneline != '\n') oneline++; - len = eol - oneline; format_subject(&subject, oneline, " "); buffer = strbuf_detach(&subject, NULL); diff --git a/builtin-update-index.c b/builtin-update-index.c index dd43d5bef..1fde893cf 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -195,7 +195,7 @@ static int process_path(const char *path) struct stat st; len = strlen(path); - if (has_symlink_leading_path(len, path)) + if (has_symlink_leading_path(path, len)) return error("'%s' is beyond a symbolic link", path); /* @@ -140,8 +140,8 @@ struct ondisk_cache_entry_extended { }; struct cache_entry { - unsigned int ce_ctime; - unsigned int ce_mtime; + struct cache_time ce_ctime; + struct cache_time ce_mtime; unsigned int ce_dev; unsigned int ce_ino; unsigned int ce_mode; @@ -282,7 +282,7 @@ struct index_state { struct cache_entry **cache; unsigned int cache_nr, cache_alloc, cache_changed; struct cache_tree *cache_tree; - time_t timestamp; + struct cache_time timestamp; void *alloc; unsigned name_hash_initialized : 1, initialized : 1; @@ -428,7 +428,7 @@ extern int read_index_preload(struct index_state *, const char **pathspec); extern int read_index_from(struct index_state *, const char *path); extern int is_index_unborn(struct index_state *); extern int read_index_unmerged(struct index_state *); -extern int write_index(const struct index_state *, int newfd); +extern int write_index(struct index_state *, int newfd); extern int discard_index(struct index_state *); extern int unmerged_index(const struct index_state *); extern int verify_path(const char *path); @@ -443,6 +443,7 @@ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int opt extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really); extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name); extern int remove_index_entry_at(struct index_state *, int pos); +extern void remove_marked_cache_entries(struct index_state *istate); extern int remove_file_from_index(struct index_state *, const char *path); #define ADD_CACHE_VERBOSE 1 #define ADD_CACHE_PRETEND 2 @@ -725,11 +726,13 @@ struct checkout { }; extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath); -extern int has_symlink_leading_path(int len, const char *name); -extern int has_symlink_or_noent_leading_path(int len, const char *name); -extern int has_dirs_only_path(int len, const char *name, int prefix_len); -extern void invalidate_lstat_cache(int len, const char *name); +extern int has_symlink_leading_path(const char *name, int len); +extern int has_symlink_or_noent_leading_path(const char *name, int len); +extern int has_dirs_only_path(const char *name, int len, int prefix_len); +extern void invalidate_lstat_cache(const char *name, int len); extern void clear_lstat_cache(void); +extern void schedule_dir_for_removal(const char *name, int len); +extern void remove_scheduled_dirs(void); extern struct alternate_object_database { struct alternate_object_database *next; @@ -802,7 +805,7 @@ struct ref { #define REF_HEADS (1u << 1) #define REF_TAGS (1u << 2) -extern struct ref *find_ref_by_name(struct ref *list, const char *name); +extern struct ref *find_ref_by_name(const struct ref *list, const char *name); #define CONNECT_VERBOSE (1u << 0) extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags); diff --git a/combine-diff.c b/combine-diff.c index b3b86aebc..066ce841e 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -712,9 +712,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent, result_size = buf.len; result = strbuf_detach(&buf, NULL); elem->mode = canon_mode(st.st_mode); - } - else if (0 <= (fd = open(elem->path, O_RDONLY)) && - !fstat(fd, &st)) { + } else if (0 <= (fd = open(elem->path, O_RDONLY))) { size_t len = xsize_t(st.st_size); ssize_t done; int is_file, i; diff --git a/compat/mingw.c b/compat/mingw.c index 27bcf3fd6..171fa85e4 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -4,6 +4,119 @@ unsigned int _CRT_fmode = _O_BINARY; +static int err_win_to_posix(DWORD winerr) +{ + int error = ENOSYS; + switch(winerr) { + case ERROR_ACCESS_DENIED: error = EACCES; break; + case ERROR_ACCOUNT_DISABLED: error = EACCES; break; + case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break; + case ERROR_ALREADY_ASSIGNED: error = EBUSY; break; + case ERROR_ALREADY_EXISTS: error = EEXIST; break; + case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break; + case ERROR_BAD_COMMAND: error = EIO; break; + case ERROR_BAD_DEVICE: error = ENODEV; break; + case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break; + case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break; + case ERROR_BAD_FORMAT: error = ENOEXEC; break; + case ERROR_BAD_LENGTH: error = EINVAL; break; + case ERROR_BAD_PATHNAME: error = ENOENT; break; + case ERROR_BAD_PIPE: error = EPIPE; break; + case ERROR_BAD_UNIT: error = ENODEV; break; + case ERROR_BAD_USERNAME: error = EINVAL; break; + case ERROR_BROKEN_PIPE: error = EPIPE; break; + case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break; + case ERROR_BUSY: error = EBUSY; break; + case ERROR_BUSY_DRIVE: error = EBUSY; break; + case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break; + case ERROR_CANNOT_MAKE: error = EACCES; break; + case ERROR_CANTOPEN: error = EIO; break; + case ERROR_CANTREAD: error = EIO; break; + case ERROR_CANTWRITE: error = EIO; break; + case ERROR_CRC: error = EIO; break; + case ERROR_CURRENT_DIRECTORY: error = EACCES; break; + case ERROR_DEVICE_IN_USE: error = EBUSY; break; + case ERROR_DEV_NOT_EXIST: error = ENODEV; break; + case ERROR_DIRECTORY: error = EINVAL; break; + case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break; + case ERROR_DISK_CHANGE: error = EIO; break; + case ERROR_DISK_FULL: error = ENOSPC; break; + case ERROR_DRIVE_LOCKED: error = EBUSY; break; + case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break; + case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break; + case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break; + case ERROR_FILE_EXISTS: error = EEXIST; break; + case ERROR_FILE_INVALID: error = ENODEV; break; + case ERROR_FILE_NOT_FOUND: error = ENOENT; break; + case ERROR_GEN_FAILURE: error = EIO; break; + case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break; + case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break; + case ERROR_INVALID_ACCESS: error = EACCES; break; + case ERROR_INVALID_ADDRESS: error = EFAULT; break; + case ERROR_INVALID_BLOCK: error = EFAULT; break; + case ERROR_INVALID_DATA: error = EINVAL; break; + case ERROR_INVALID_DRIVE: error = ENODEV; break; + case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break; + case ERROR_INVALID_FLAGS: error = EINVAL; break; + case ERROR_INVALID_FUNCTION: error = ENOSYS; break; + case ERROR_INVALID_HANDLE: error = EBADF; break; + case ERROR_INVALID_LOGON_HOURS: error = EACCES; break; + case ERROR_INVALID_NAME: error = EINVAL; break; + case ERROR_INVALID_OWNER: error = EINVAL; break; + case ERROR_INVALID_PARAMETER: error = EINVAL; break; + case ERROR_INVALID_PASSWORD: error = EPERM; break; + case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break; + case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break; + case ERROR_INVALID_TARGET_HANDLE: error = EIO; break; + case ERROR_INVALID_WORKSTATION: error = EACCES; break; + case ERROR_IO_DEVICE: error = EIO; break; + case ERROR_IO_INCOMPLETE: error = EINTR; break; + case ERROR_LOCKED: error = EBUSY; break; + case ERROR_LOCK_VIOLATION: error = EACCES; break; + case ERROR_LOGON_FAILURE: error = EACCES; break; + case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break; + case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break; + case ERROR_MORE_DATA: error = EPIPE; break; + case ERROR_NEGATIVE_SEEK: error = ESPIPE; break; + case ERROR_NOACCESS: error = EFAULT; break; + case ERROR_NONE_MAPPED: error = EINVAL; break; + case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break; + case ERROR_NOT_READY: error = EAGAIN; break; + case ERROR_NOT_SAME_DEVICE: error = EXDEV; break; + case ERROR_NO_DATA: error = EPIPE; break; + case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break; + case ERROR_NO_PROC_SLOTS: error = EAGAIN; break; + case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break; + case ERROR_OPEN_FAILED: error = EIO; break; + case ERROR_OPEN_FILES: error = EBUSY; break; + case ERROR_OPERATION_ABORTED: error = EINTR; break; + case ERROR_OUTOFMEMORY: error = ENOMEM; break; + case ERROR_PASSWORD_EXPIRED: error = EACCES; break; + case ERROR_PATH_BUSY: error = EBUSY; break; + case ERROR_PATH_NOT_FOUND: error = ENOENT; break; + case ERROR_PIPE_BUSY: error = EBUSY; break; + case ERROR_PIPE_CONNECTED: error = EPIPE; break; + case ERROR_PIPE_LISTENING: error = EPIPE; break; + case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break; + case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break; + case ERROR_READ_FAULT: error = EIO; break; + case ERROR_SEEK: error = EIO; break; + case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break; + case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break; + case ERROR_SHARING_VIOLATION: error = EACCES; break; + case ERROR_STACK_OVERFLOW: error = ENOMEM; break; + case ERROR_SWAPERROR: error = ENOENT; break; + case ERROR_TOO_MANY_MODULES: error = EMFILE; break; + case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break; + case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break; + case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break; + case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break; + case ERROR_WRITE_FAULT: error = EIO; break; + case ERROR_WRITE_PROTECT: error = EROFS; break; + } + return error; +} + #undef open int mingw_open (const char *filename, int oflags, ...) { @@ -1005,3 +1118,24 @@ void mingw_open_html(const char *unixpath) printf("Launching default browser to display HTML ...\n"); ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0); } + +int link(const char *oldpath, const char *newpath) +{ + typedef BOOL WINAPI (*T)(const char*, const char*, LPSECURITY_ATTRIBUTES); + static T create_hard_link = NULL; + if (!create_hard_link) { + create_hard_link = (T) GetProcAddress( + GetModuleHandle("kernel32.dll"), "CreateHardLinkA"); + if (!create_hard_link) + create_hard_link = (T)-1; + } + if (create_hard_link == (T)-1) { + errno = ENOSYS; + return -1; + } + if (!create_hard_link(newpath, oldpath, NULL)) { + errno = err_win_to_posix(GetLastError()); + return -1; + } + return 0; +} diff --git a/compat/mingw.h b/compat/mingw.h index 6e2468644..762eb143a 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -67,8 +67,6 @@ static inline int readlink(const char *path, char *buf, size_t bufsiz) { errno = ENOSYS; return -1; } static inline int symlink(const char *oldpath, const char *newpath) { errno = ENOSYS; return -1; } -static inline int link(const char *oldpath, const char *newpath) -{ errno = ENOSYS; return -1; } static inline int fchmod(int fildes, mode_t mode) { errno = ENOSYS; return -1; } static inline int fork(void) @@ -134,6 +132,7 @@ int getpagesize(void); /* defined in MinGW's libgcc.a */ struct passwd *getpwuid(int uid); int setitimer(int type, struct itimerval *in, struct itimerval *out); int sigaction(int sig, struct sigaction *in, struct sigaction *out); +int link(const char *oldpath, const char *newpath); /* * replacements of existing functions @@ -160,6 +159,11 @@ int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz); int mingw_rename(const char*, const char*); #define rename mingw_rename +#ifdef USE_WIN32_MMAP +int mingw_getpagesize(void); +#define getpagesize mingw_getpagesize +#endif + /* Use mingw_lstat() instead of lstat()/stat() and * mingw_fstat() instead of fstat() on Windows. */ diff --git a/compat/win32mmap.c b/compat/win32mmap.c new file mode 100644 index 000000000..779d796cd --- /dev/null +++ b/compat/win32mmap.c @@ -0,0 +1,53 @@ +#include "../git-compat-util.h" + +/* + * Note that this doesn't return the actual pagesize, but + * the allocation granularity. If future Windows specific git code + * needs the real getpagesize function, we need to find another solution. + */ +int mingw_getpagesize(void) +{ + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwAllocationGranularity; +} + +void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) +{ + HANDLE hmap; + void *temp; + size_t len; + struct stat st; + uint64_t o = offset; + uint32_t l = o & 0xFFFFFFFF; + uint32_t h = (o >> 32) & 0xFFFFFFFF; + + if (!fstat(fd, &st)) + len = xsize_t(st.st_size); + else + die("mmap: could not determine filesize"); + + if ((length + offset) > len) + length = len - offset; + + if (!(flags & MAP_PRIVATE)) + die("Invalid usage of mmap when built with USE_WIN32_MMAP"); + + hmap = CreateFileMapping((HANDLE)_get_osfhandle(fd), 0, PAGE_WRITECOPY, + 0, 0, 0); + + if (!hmap) + return MAP_FAILED; + + temp = MapViewOfFileEx(hmap, FILE_MAP_COPY, h, l, length, start); + + if (!CloseHandle(hmap)) + warning("unable to close file mapping handle\n"); + + return temp ? temp : MAP_FAILED; +} + +int git_munmap(void *start, size_t length) +{ + return !UnmapViewOfFile(start); +} diff --git a/compat/winansi.c b/compat/winansi.c index e2d96dfe6..44dc293ad 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -18,8 +18,6 @@ This file is git-specific. Therefore, this file does not attempt to implement any codes that are not used by git. - - TODO: K */ static HANDLE console; @@ -79,6 +77,20 @@ static void set_console_attr(void) SetConsoleTextAttribute(console, attributes); } +static void erase_in_line(void) +{ + CONSOLE_SCREEN_BUFFER_INFO sbi; + + if (!console) + return; + + GetConsoleScreenBufferInfo(console, &sbi); + FillConsoleOutputCharacterA(console, ' ', + sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition, + NULL); +} + + static const char *set_attr(const char *str) { const char *func; @@ -218,7 +230,7 @@ static const char *set_attr(const char *str) set_console_attr(); break; case 'K': - /* TODO */ + erase_in_line(); break; default: /* Unsupported code */ @@ -644,28 +644,37 @@ int git_config_global(void) int git_config(config_fn_t fn, void *data) { - int ret = 0; + int ret = 0, found = 0; char *repo_config = NULL; const char *home = NULL; /* Setting $GIT_CONFIG makes git read _only_ the given config file. */ if (config_exclusive_filename) return git_config_from_file(fn, config_exclusive_filename, data); - if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) + if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) { ret += git_config_from_file(fn, git_etc_gitconfig(), data); + found += 1; + } home = getenv("HOME"); if (git_config_global() && home) { char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); - if (!access(user_config, R_OK)) + if (!access(user_config, R_OK)) { ret += git_config_from_file(fn, user_config, data); + found += 1; + } free(user_config); } repo_config = git_pathdup("config"); - ret += git_config_from_file(fn, repo_config, data); + if (!access(repo_config, R_OK)) { + ret += git_config_from_file(fn, repo_config, data); + found += 1; + } free(repo_config); + if (found == 0) + return -1; return ret; } @@ -504,7 +504,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig, const char *prog, int flags) { char *url = xstrdup(url_orig); - char *host, *path = url; + char *host, *path; char *end; int c; struct child_process *conn; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 271b911f7..ed235f759 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1516,7 +1516,7 @@ _git_config () _git_remote () { - local subcommands="add rename rm show prune update" + local subcommands="add rename rm show prune update set-head" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool index 0cda3d2ee..0deda3a0e 100755 --- a/contrib/difftool/git-difftool +++ b/contrib/difftool/git-difftool @@ -4,7 +4,7 @@ # This is a wrapper around the GIT_EXTERNAL_DIFF-compatible # git-difftool-helper script. This script exports # GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and -# GIT_DIFFTOOL_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper. +# GIT_DIFFTOOL_NO_PROMPT and GIT_DIFF_TOOL for use by git-difftool-helper. # Any arguments that are unknown to this script are forwarded to 'git diff'. use strict; @@ -49,12 +49,12 @@ sub generate_command } if ($arg eq '-t' or $arg eq '--tool') { usage() if $#ARGV <= $idx; - $ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1]; + $ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1]; $skip_next = 1; next; } if ($arg =~ /^--tool=/) { - $ENV{GIT_MERGE_TOOL} = substr($arg, 7); + $ENV{GIT_DIFF_TOOL} = substr($arg, 7); next; } if ($arg eq '--no-prompt') { diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index db3af6a83..9c0a13452 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -128,8 +128,10 @@ launch_merge_tool () { cleanup_temp_files } -# Verifies that mergetool.<tool>.cmd exists +# Verifies that (difftool|mergetool).<tool>.cmd exists valid_custom_tool() { + merge_tool_cmd="$(git config difftool.$1.cmd)" + test -z "$merge_tool_cmd" && merge_tool_cmd="$(git config mergetool.$1.cmd)" test -n "$merge_tool_cmd" } @@ -150,8 +152,11 @@ valid_tool() { } # Sets up the merge_tool_path variable. -# This handles the mergetool.<tool>.path configuration. +# This handles the difftool.<tool>.path configuration. +# This also falls back to mergetool defaults. init_merge_tool_path() { + merge_tool_path=$(git config difftool."$1".path) + test -z "$merge_tool_path" && merge_tool_path=$(git config mergetool."$1".path) if test -z "$merge_tool_path"; then case "$1" in @@ -165,15 +170,19 @@ init_merge_tool_path() { fi } -# Allow the GIT_MERGE_TOOL variable to provide a default value +# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL" +test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL" -# If not merge tool was specified then use the merge.tool +# If merge tool was not specified then use the diff.tool # configuration variable. If that's invalid then reset merge_tool. +# Fallback to merge.tool. if test -z "$merge_tool"; then + merge_tool=$(git config diff.tool) + test -z "$merge_tool" && merge_tool=$(git config merge.tool) if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then - echo >&2 "git config option merge.tool set to unknown tool: $merge_tool" + echo >&2 "git config option diff.tool set to unknown tool: $merge_tool" echo >&2 "Resetting to default..." unset merge_tool fi diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt index 6e2610cda..2b7bc03ec 100644 --- a/contrib/difftool/git-difftool.txt +++ b/contrib/difftool/git-difftool.txt @@ -32,23 +32,23 @@ OPTIONS vimdiff, gvimdiff, ecmerge, and opendiff + If a merge resolution program is not specified, 'git-difftool' -will use the configuration variable `merge.tool`. If the -configuration variable `merge.tool` is not set, 'git difftool' +will use the configuration variable `diff.tool`. If the +configuration variable `diff.tool` is not set, 'git-difftool' will pick a suitable default. + You can explicitly provide a full path to the tool by setting the -configuration variable `mergetool.<tool>.path`. For example, you +configuration variable `difftool.<tool>.path`. For example, you can configure the absolute path to kdiff3 by setting -`mergetool.kdiff3.path`. Otherwise, 'git-difftool' assumes the +`difftool.kdiff3.path`. Otherwise, 'git-difftool' assumes the tool is available in PATH. + Instead of running one of the known merge tool programs, 'git-difftool' can be customized to run an alternative program by specifying the command line to invoke in a configuration -variable `mergetool.<tool>.cmd`. +variable `difftool.<tool>.cmd`. + When 'git-difftool' is invoked with this tool (either through the -`-t` or `--tool` option or the `merge.tool` configuration variable) +`-t` or `--tool` option or the `diff.tool` configuration variable) the configured command line will be invoked with the following variables available: `$LOCAL` is set to the name of the temporary file containing the contents of the diff pre-image and `$REMOTE` @@ -61,24 +61,24 @@ with custom merge tool commands and has the same value as `$LOCAL`. CONFIG VARIABLES ---------------- -merge.tool:: - The default merge tool to use. -+ -See the `--tool=<tool>` option above for more details. +'git-difftool' falls back to 'git-mergetool' config variables when the +difftool equivalents have not been defined. -merge.keepBackup:: - The original, unedited file content can be saved to a file with - a `.orig` extension. Defaults to `true` (i.e. keep the backup files). +diff.tool:: + The default merge tool to use. -mergetool.<tool>.path:: +difftool.<tool>.path:: Override the path for the given tool. This is useful in case your tool is not in the PATH. -mergetool.<tool>.cmd:: +difftool.<tool>.cmd:: Specify the command to invoke the specified merge tool. + See the `--tool=<tool>` option above for more details. +merge.keepBackup:: + The original, unedited file content can be saved to a file with + a `.orig` extension. Defaults to `true` (i.e. keep the backup files). SEE ALSO -------- diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 23aeb257b..6309d146e 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -14,13 +14,18 @@ die "usage: import-tars *.tar.{gz,bz2,Z}\n" unless @ARGV; my $branch_name = 'import-tars'; my $branch_ref = "refs/heads/$branch_name"; -my $committer_name = 'T Ar Creator'; -my $committer_email = 'tar@example.com'; +my $author_name = $ENV{'GIT_AUTHOR_NAME'} || 'T Ar Creator'; +my $author_email = $ENV{'GIT_AUTHOR_EMAIL'} || 'tar@example.com'; +my $committer_name = $ENV{'GIT_COMMITTER_NAME'} || `git config --get user.name`; +my $committer_email = $ENV{'GIT_COMMITTER_EMAIL'} || `git config --get user.email`; + +chomp($committer_name, $committer_email); open(FI, '|-', 'git', 'fast-import', '--quiet') or die "Unable to start git fast-import: $!\n"; foreach my $tar_file (@ARGV) { + my $commit_time = time; $tar_file =~ m,([^/]+)$,; my $tar_name = $1; @@ -39,7 +44,7 @@ foreach my $tar_file (@ARGV) die "Unrecognized compression format: $tar_file\n"; } - my $commit_time = 0; + my $author_time = 0; my $next_mark = 1; my $have_top_dir = 1; my ($top_dir, %files); @@ -92,7 +97,7 @@ foreach my $tar_file (@ARGV) } $files{$path} = [$next_mark++, $mode]; - $commit_time = $mtime if $mtime > $commit_time; + $author_time = $mtime if $mtime > $author_time; $path =~ m,^([^/]+)/,; $top_dir = $1 unless $top_dir; $have_top_dir = 0 if $top_dir ne $1; @@ -100,6 +105,7 @@ foreach my $tar_file (@ARGV) print FI <<EOF; commit $branch_ref +author $author_name <$author_email> $author_time +0000 committer $committer_name <$committer_email> $commit_time +0000 data <<END_OF_COMMIT_MESSAGE Imported from $tar_file. @@ -119,7 +125,7 @@ EOF print FI <<EOF; tag $tar_name from $branch_ref -tagger $committer_name <$committer_email> $commit_time +0000 +tagger $author_name <$author_email> $author_time +0000 data <<END_OF_TAG_MESSAGE Package $tar_name END_OF_TAG_MESSAGE diff --git a/diff-lib.c b/diff-lib.c index 79d060683..a310fb2ad 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -31,7 +31,7 @@ static int check_removed(const struct cache_entry *ce, struct stat *st) return -1; return 1; } - if (has_symlink_leading_path(ce_namelen(ce), ce->name)) + if (has_symlink_leading_path(ce->name, ce_namelen(ce))) return 1; if (S_ISDIR(st->st_mode)) { unsigned char sub[20]; diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c index 574b3e833..d0ef83970 100644 --- a/diffcore-pickaxe.c +++ b/diffcore-pickaxe.c @@ -25,10 +25,12 @@ static unsigned int contains(struct diff_filespec *one, regmatch_t regmatch; int flags = 0; + assert(data[sz] == '\0'); while (*data && !regexec(regexp, data, 1, ®match, flags)) { flags |= REG_NOTBOL; - data += regmatch.rm_so; - if (*data) data++; + data += regmatch.rm_eo; + if (*data && regmatch.rm_so == regmatch.rm_eo) + data++; cnt++; } @@ -720,7 +720,7 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i { struct path_simplify *simplify; - if (has_symlink_leading_path(strlen(path), path)) + if (has_symlink_leading_path(path, strlen(path))) return dir->nr; simplify = create_simplify(pathspec); @@ -2,15 +2,19 @@ #include "blob.h" #include "dir.h" -static void create_directories(const char *path, const struct checkout *state) +static void create_directories(const char *path, int path_len, + const struct checkout *state) { - int len = strlen(path); - char *buf = xmalloc(len + 1); - const char *slash = path; - - while ((slash = strchr(slash+1, '/')) != NULL) { - len = slash - path; - memcpy(buf, path, len); + char *buf = xmalloc(path_len + 1); + int len = 0; + + while (len < path_len) { + do { + buf[len] = path[len]; + len++; + } while (len < path_len && path[len] != '/'); + if (len >= path_len) + break; buf[len] = 0; /* @@ -20,7 +24,7 @@ static void create_directories(const char *path, const struct checkout *state) * we test the path components of the prefix with the * stat() function instead of the lstat() function. */ - if (has_dirs_only_path(len, buf, state->base_dir_len)) + if (has_dirs_only_path(buf, len, state->base_dir_len)) continue; /* ok, it is already a directory. */ /* @@ -74,7 +78,7 @@ static int create_file(const char *path, unsigned int mode) return open(path, O_WRONLY | O_CREAT | O_EXCL, mode); } -static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size) +static void *read_blob_entry(struct cache_entry *ce, unsigned long *size) { enum object_type type; void *new = read_sha1_file(ce->sha1, &type, size); @@ -89,36 +93,52 @@ static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile) { - int fd; - long wrote; - - switch (ce->ce_mode & S_IFMT) { - char *new; - struct strbuf buf; - unsigned long size; + unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT; + int fd, ret, fstat_done = 0; + char *new; + struct strbuf buf = STRBUF_INIT; + unsigned long size; + size_t wrote, newsize = 0; + struct stat st; + switch (ce_mode_s_ifmt) { case S_IFREG: - new = read_blob_entry(ce, path, &size); + case S_IFLNK: + new = read_blob_entry(ce, &size); if (!new) return error("git checkout-index: unable to read sha1 file of %s (%s)", path, sha1_to_hex(ce->sha1)); + if (ce_mode_s_ifmt == S_IFLNK && has_symlinks && !to_tempfile) { + ret = symlink(new, path); + free(new); + if (ret) + return error("git checkout-index: unable to create symlink %s (%s)", + path, strerror(errno)); + break; + } + /* * Convert from git internal format to working tree format */ - strbuf_init(&buf, 0); - if (convert_to_working_tree(ce->name, new, size, &buf)) { - size_t newsize = 0; + if (ce_mode_s_ifmt == S_IFREG && + convert_to_working_tree(ce->name, new, size, &buf)) { free(new); new = strbuf_detach(&buf, &newsize); size = newsize; } if (to_tempfile) { - strcpy(path, ".merge_file_XXXXXX"); + if (ce_mode_s_ifmt == S_IFREG) + strcpy(path, ".merge_file_XXXXXX"); + else + strcpy(path, ".merge_link_XXXXXX"); fd = mkstemp(path); - } else + } else if (ce_mode_s_ifmt == S_IFREG) { fd = create_file(path, ce->ce_mode); + } else { + fd = create_file(path, 0666); + } if (fd < 0) { free(new); return error("git checkout-index: unable to create file %s (%s)", @@ -126,41 +146,16 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout } wrote = write_in_full(fd, new, size); + /* use fstat() only when path == ce->name */ + if (state->refresh_cache && !to_tempfile && !state->base_dir_len) { + fstat(fd, &st); + fstat_done = 1; + } close(fd); free(new); if (wrote != size) return error("git checkout-index: unable to write file %s", path); break; - case S_IFLNK: - new = read_blob_entry(ce, path, &size); - if (!new) - return error("git checkout-index: unable to read sha1 file of %s (%s)", - path, sha1_to_hex(ce->sha1)); - if (to_tempfile || !has_symlinks) { - if (to_tempfile) { - strcpy(path, ".merge_link_XXXXXX"); - fd = mkstemp(path); - } else - fd = create_file(path, 0666); - if (fd < 0) { - free(new); - return error("git checkout-index: unable to create " - "file %s (%s)", path, strerror(errno)); - } - wrote = write_in_full(fd, new, size); - close(fd); - free(new); - if (wrote != size) - return error("git checkout-index: unable to write file %s", - path); - } else { - wrote = symlink(new, path); - free(new); - if (wrote) - return error("git checkout-index: unable to create " - "symlink %s (%s)", path, strerror(errno)); - } - break; case S_IFGITLINK: if (to_tempfile) return error("git checkout-index: cannot create temporary subproject %s", path); @@ -172,8 +167,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout } if (state->refresh_cache) { - struct stat st; - lstat(ce->name, &st); + if (!fstat_done) + lstat(ce->name, &st); fill_stat_cache_info(ce, &st); } return 0; @@ -190,6 +185,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t memcpy(path, state->base_dir, len); strcpy(path + len, ce->name); + len += ce_namelen(ce); if (!lstat(path, &st)) { unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID); @@ -218,6 +214,6 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t return error("unable to unlink old '%s' (%s)", path, strerror(errno)); } else if (state->not_new) return 0; - create_directories(path, state); + create_directories(path, len, state); return write_entry(ce, path, state, 0); } diff --git a/git-compat-util.h b/git-compat-util.h index 878d83dd0..f09f24406 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -166,7 +166,7 @@ static inline const char *skip_prefix(const char *str, const char *prefix) return strncmp(str, prefix, len) ? NULL : str + len; } -#ifdef NO_MMAP +#if defined(NO_MMAP) || defined(USE_WIN32_MMAP) #ifndef PROT_READ #define PROT_READ 1 @@ -180,13 +180,19 @@ static inline const char *skip_prefix(const char *str, const char *prefix) extern void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); extern int git_munmap(void *start, size_t length); +#else /* NO_MMAP || USE_WIN32_MMAP */ + +#include <sys/mman.h> + +#endif /* NO_MMAP || USE_WIN32_MMAP */ + +#ifdef NO_MMAP + /* This value must be multiple of (pagesize * 2) */ #define DEFAULT_PACKED_GIT_WINDOW_SIZE (1 * 1024 * 1024) #else /* NO_MMAP */ -#include <sys/mman.h> - /* This value must be multiple of (pagesize * 2) */ #define DEFAULT_PACKED_GIT_WINDOW_SIZE \ (sizeof(void*) >= 8 \ @@ -388,4 +394,18 @@ void git_qsort(void *base, size_t nmemb, size_t size, # define FORCE_DIR_SET_GID 0 #endif +#ifdef NO_NSEC +#undef USE_NSEC +#define ST_CTIME_NSEC(st) 0 +#define ST_MTIME_NSEC(st) 0 +#else +#ifdef USE_ST_TIMESPEC +#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec)) +#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec)) +#else +#define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec)) +#define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec)) +#endif +#endif + #endif diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 3dc659dd5..314cd364b 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -442,6 +442,30 @@ do_rest () { done } +# skip picking commits whose parents are unchanged +skip_unnecessary_picks () { + fd=3 + while read command sha1 rest + do + # fd=3 means we skip the command + case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in + 3,pick,"$ONTO"*|3,p,"$ONTO"*) + # pick a commit whose parent is current $ONTO -> skip + ONTO=$sha1 + ;; + 3,#*|3,,*) + # copy comments + ;; + *) + fd=1 + ;; + esac + echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd + done <"$TODO" >"$TODO.new" 3>>"$DONE" && + mv -f "$TODO".new "$TODO" || + die "Could not skip unnecessary pick commands" +} + # check if no other options are set is_standalone () { test $# -eq 2 -a "$2" = '--' && @@ -746,6 +770,8 @@ EOF has_action "$TODO" || die_abort "Nothing to do" + test -d "$REWRITTEN" || skip_unnecessary_picks + git update-ref ORIG_HEAD $HEAD output git checkout $ONTO && do_rest ;; diff --git a/git-rebase.sh b/git-rebase.sh index d38ab0b83..0ade69922 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -309,6 +309,10 @@ do ;; esac ;; + --committer-date-is-author-date|--ignore-date) + git_am_opt="$git_am_opt $1" + force_rebase=t + ;; -C*) git_am_opt="$git_am_opt $1" ;; diff --git a/git-send-email.perl b/git-send-email.perl index 57127aa82..546d2ebc0 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -821,7 +821,7 @@ Date: $date Message-Id: $message_id X-Mailer: git-send-email $gitversion "; - if ($thread && $reply_to) { + if ($reply_to) { $header .= "In-Reply-To: $reply_to\n"; $header .= "References: $references\n"; @@ -39,6 +39,8 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt) { int err; + p->word_regexp = opt->word_regexp; + if (opt->fixed || is_fixed(p->pattern)) p->fixed = 1; if (opt->regflags & REG_ICASE) @@ -190,7 +192,8 @@ void compile_grep_patterns(struct grep_opt *opt) * A classic recursive descent parser would do. */ p = opt->pattern_list; - opt->pattern_expression = compile_pattern_expr(&p); + if (p) + opt->pattern_expression = compile_pattern_expr(&p); if (p) die("incomplete pattern expression: %s", p->pattern); } @@ -251,18 +254,6 @@ static int word_char(char ch) return isalnum(ch) || ch == '_'; } -static void show_line(struct grep_opt *opt, const char *bol, const char *eol, - const char *name, unsigned lno, char sign) -{ - if (opt->null_following_name) - sign = '\0'; - if (opt->pathname) - printf("%s%c", name, sign); - if (opt->linenum) - printf("%d%c", lno, sign); - printf("%.*s\n", (int)(eol-bol), bol); -} - static void show_name(struct grep_opt *opt, const char *name) { printf("%s%c", name, opt->null_following_name ? '\0' : '\n'); @@ -306,11 +297,12 @@ static struct { { "committer ", 10 }, }; -static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx) +static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, + enum grep_context ctx, + regmatch_t *pmatch, int eflags) { int hit = 0; int saved_ch = 0; - regmatch_t pmatch[10]; if ((p->token != GREP_PATTERN) && ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD))) @@ -329,16 +321,12 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol } again: - if (!p->fixed) { - regex_t *exp = &p->regexp; - hit = !regexec(exp, bol, ARRAY_SIZE(pmatch), - pmatch, 0); - } - else { + if (p->fixed) hit = !fixmatch(p->pattern, bol, pmatch); - } + else + hit = !regexec(&p->regexp, bol, 1, pmatch, eflags); - if (hit && opt->word_regexp) { + if (hit && p->word_regexp) { if ((pmatch[0].rm_so < 0) || (eol - bol) <= pmatch[0].rm_so || (pmatch[0].rm_eo < 0) || @@ -378,39 +366,33 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol return hit; } -static int match_expr_eval(struct grep_opt *o, - struct grep_expr *x, - char *bol, char *eol, - enum grep_context ctx, - int collect_hits) +static int match_expr_eval(struct grep_expr *x, char *bol, char *eol, + enum grep_context ctx, int collect_hits) { int h = 0; + regmatch_t match; switch (x->node) { case GREP_NODE_ATOM: - h = match_one_pattern(o, x->u.atom, bol, eol, ctx); + h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0); break; case GREP_NODE_NOT: - h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0); + h = !match_expr_eval(x->u.unary, bol, eol, ctx, 0); break; case GREP_NODE_AND: - if (!collect_hits) - return (match_expr_eval(o, x->u.binary.left, - bol, eol, ctx, 0) && - match_expr_eval(o, x->u.binary.right, - bol, eol, ctx, 0)); - h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0); - h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0); + if (!match_expr_eval(x->u.binary.left, bol, eol, ctx, 0)) + return 0; + h = match_expr_eval(x->u.binary.right, bol, eol, ctx, 0); break; case GREP_NODE_OR: if (!collect_hits) - return (match_expr_eval(o, x->u.binary.left, + return (match_expr_eval(x->u.binary.left, bol, eol, ctx, 0) || - match_expr_eval(o, x->u.binary.right, + match_expr_eval(x->u.binary.right, bol, eol, ctx, 0)); - h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0); + h = match_expr_eval(x->u.binary.left, bol, eol, ctx, 0); x->u.binary.left->hit |= h; - h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1); + h |= match_expr_eval(x->u.binary.right, bol, eol, ctx, 1); break; default: die("Unexpected node type (internal error) %d", x->node); @@ -424,24 +406,104 @@ static int match_expr(struct grep_opt *opt, char *bol, char *eol, enum grep_context ctx, int collect_hits) { struct grep_expr *x = opt->pattern_expression; - return match_expr_eval(opt, x, bol, eol, ctx, collect_hits); + return match_expr_eval(x, bol, eol, ctx, collect_hits); } static int match_line(struct grep_opt *opt, char *bol, char *eol, enum grep_context ctx, int collect_hits) { struct grep_pat *p; + regmatch_t match; + if (opt->extended) return match_expr(opt, bol, eol, ctx, collect_hits); /* we do not call with collect_hits without being extended */ for (p = opt->pattern_list; p; p = p->next) { - if (match_one_pattern(opt, p, bol, eol, ctx)) + if (match_one_pattern(p, bol, eol, ctx, &match, 0)) return 1; } return 0; } +static int match_next_pattern(struct grep_pat *p, char *bol, char *eol, + enum grep_context ctx, + regmatch_t *pmatch, int eflags) +{ + regmatch_t match; + + if (!match_one_pattern(p, bol, eol, ctx, &match, eflags)) + return 0; + if (match.rm_so < 0 || match.rm_eo < 0) + return 0; + if (pmatch->rm_so >= 0 && pmatch->rm_eo >= 0) { + if (match.rm_so > pmatch->rm_so) + return 1; + if (match.rm_so == pmatch->rm_so && match.rm_eo < pmatch->rm_eo) + return 1; + } + pmatch->rm_so = match.rm_so; + pmatch->rm_eo = match.rm_eo; + return 1; +} + +static int next_match(struct grep_opt *opt, char *bol, char *eol, + enum grep_context ctx, regmatch_t *pmatch, int eflags) +{ + struct grep_pat *p; + int hit = 0; + + pmatch->rm_so = pmatch->rm_eo = -1; + if (bol < eol) { + for (p = opt->pattern_list; p; p = p->next) { + switch (p->token) { + case GREP_PATTERN: /* atom */ + case GREP_PATTERN_HEAD: + case GREP_PATTERN_BODY: + hit |= match_next_pattern(p, bol, eol, ctx, + pmatch, eflags); + break; + default: + break; + } + } + } + return hit; +} + +static void show_line(struct grep_opt *opt, char *bol, char *eol, + const char *name, unsigned lno, char sign) +{ + int rest = eol - bol; + + if (opt->null_following_name) + sign = '\0'; + if (opt->pathname) + printf("%s%c", name, sign); + if (opt->linenum) + printf("%d%c", lno, sign); + if (opt->color) { + regmatch_t match; + enum grep_context ctx = GREP_CONTEXT_BODY; + int ch = *eol; + int eflags = 0; + + *eol = '\0'; + while (next_match(opt, bol, eol, ctx, &match, eflags)) { + printf("%.*s%s%.*s%s", + (int)match.rm_so, bol, + opt->color_match, + (int)(match.rm_eo - match.rm_so), bol + match.rm_so, + GIT_COLOR_RESET); + bol += match.rm_eo; + rest -= match.rm_eo; + eflags = REG_NOTBOL; + } + *eol = ch; + } + printf("%.*s\n", rest, bol); +} + static int grep_buffer_1(struct grep_opt *opt, const char *name, char *buf, unsigned long size, int collect_hits) { @@ -1,5 +1,6 @@ #ifndef GREP_H #define GREP_H +#include "color.h" enum grep_pat_token { GREP_PATTERN, @@ -31,6 +32,7 @@ struct grep_pat { enum grep_header_field field; regex_t regexp; unsigned fixed:1; + unsigned word_regexp:1; }; enum grep_expr_node { @@ -76,6 +78,9 @@ struct grep_opt { unsigned relative:1; unsigned pathname:1; unsigned null_following_name:1; + int color; + char color_match[COLOR_MAXLEN]; + const char *color_external; int regflags; unsigned pre_context; unsigned post_context; diff --git a/http-push.c b/http-push.c index 30d2d3404..48e5f38fe 100644 --- a/http-push.c +++ b/http-push.c @@ -816,7 +816,7 @@ static void finish_request(struct transfer_request *request) #ifdef USE_CURL_MULTI static int fill_active_slot(void *unused) { - struct transfer_request *request = request_queue_head; + struct transfer_request *request; if (aborted) return 0; @@ -1792,21 +1792,8 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock) return 1; } -static struct ref *local_refs, **local_tail; static struct ref *remote_refs, **remote_tail; -static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) -{ - struct ref *ref; - int len = strlen(refname) + 1; - ref = xcalloc(1, sizeof(*ref) + len); - hashcpy(ref->new_sha1, sha1); - memcpy(ref->name, refname, len); - *local_tail = ref; - local_tail = &ref->next; - return 0; -} - static void one_remote_ref(char *refname) { struct ref *ref; @@ -1839,12 +1826,6 @@ static void one_remote_ref(char *refname) remote_tail = &ref->next; } -static void get_local_heads(void) -{ - local_tail = &local_refs; - for_each_ref(one_local_ref, NULL); -} - static void get_dav_remote_heads(void) { remote_tail = &remote_refs; @@ -1862,55 +1843,6 @@ static int is_zero_sha1(const unsigned char *sha1) return 1; } -static void unmark_and_free(struct commit_list *list, unsigned int mark) -{ - while (list) { - struct commit_list *temp = list; - temp->item->object.flags &= ~mark; - list = temp->next; - free(temp); - } -} - -static int ref_newer(const unsigned char *new_sha1, - const unsigned char *old_sha1) -{ - struct object *o; - struct commit *old, *new; - struct commit_list *list, *used; - int found = 0; - - /* Both new and old must be commit-ish and new is descendant of - * old. Otherwise we require --force. - */ - o = deref_tag(parse_object(old_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - old = (struct commit *) o; - - o = deref_tag(parse_object(new_sha1), NULL, 0); - if (!o || o->type != OBJ_COMMIT) - return 0; - new = (struct commit *) o; - - if (parse_commit(new) < 0) - return 0; - - used = list = NULL; - commit_list_insert(new, &list); - while (list) { - new = pop_most_recent_commit(&list, TMP_MARK); - commit_list_insert(new, &used); - if (new == old) { - found = 1; - break; - } - } - unmark_and_free(list, TMP_MARK); - unmark_and_free(used, TMP_MARK); - return found; -} - static void add_remote_info_ref(struct remote_ls_ctx *ls) { struct strbuf *buf = (struct strbuf *)ls->userData; @@ -2195,7 +2127,7 @@ int main(int argc, char **argv) int rc = 0; int i; int new_refs; - struct ref *ref; + struct ref *ref, *local_refs; char *rewritten_url = NULL; git_extract_argv0_path(argv[0]); @@ -2302,7 +2234,7 @@ int main(int argc, char **argv) fetch_indices(); /* Get a list of all local and remote heads to validate refspecs */ - get_local_heads(); + local_refs = get_local_heads(); fprintf(stderr, "Fetching remote heads...\n"); get_dav_remote_heads(); diff --git a/imap-send.c b/imap-send.c index cb518eb61..8154cb211 100644 --- a/imap-send.c +++ b/imap-send.c @@ -579,7 +579,7 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx, n = socket_write(&imap->buf.sock, cmd->cb.data, cmd->cb.dlen); free(cmd->cb.data); if (n != cmd->cb.dlen || - (n = socket_write(&imap->buf.sock, "\r\n", 2)) != 2) { + socket_write(&imap->buf.sock, "\r\n", 2) != 2) { free(cmd->cmd); free(cmd); return NULL; diff --git a/index-pack.c b/index-pack.c index 7fee87253..75468228d 100644 --- a/index-pack.c +++ b/index-pack.c @@ -232,7 +232,7 @@ static void free_base_data(struct base_data *c) static void prune_base_data(struct base_data *retain) { - struct base_data *b = base_cache; + struct base_data *b; for (b = base_cache; base_cache_used > delta_base_cache_limit && b; b = b->child) { diff --git a/merge-recursive.c b/merge-recursive.c index ee853b990..3e1bc3e07 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -801,22 +801,19 @@ static int process_renames(struct merge_options *o, } for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) { - int compare; char *src; - struct string_list *renames1, *renames2, *renames2Dst; + struct string_list *renames1, *renames2Dst; struct rename *ren1 = NULL, *ren2 = NULL; const char *branch1, *branch2; const char *ren1_src, *ren1_dst; if (i >= a_renames->nr) { - compare = 1; ren2 = b_renames->items[j++].util; } else if (j >= b_renames->nr) { - compare = -1; ren1 = a_renames->items[i++].util; } else { - compare = strcmp(a_renames->items[i].string, - b_renames->items[j].string); + int compare = strcmp(a_renames->items[i].string, + b_renames->items[j].string); if (compare <= 0) ren1 = a_renames->items[i++].util; if (compare >= 0) @@ -826,14 +823,12 @@ static int process_renames(struct merge_options *o, /* TODO: refactor, so that 1/2 are not needed */ if (ren1) { renames1 = a_renames; - renames2 = b_renames; renames2Dst = &b_by_dst; branch1 = o->branch1; branch2 = o->branch2; } else { struct rename *tmp; renames1 = b_renames; - renames2 = a_renames; renames2Dst = &a_by_dst; branch1 = o->branch2; branch2 = o->branch1; @@ -135,7 +135,6 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, int namelen; unsigned long time; int tz; - const char *filler = " "; if (fmt == CMIT_FMT_ONELINE) return; @@ -154,7 +153,6 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, while (line < name_tail && isspace(name_tail[-1])) name_tail--; display_name_length = name_tail - line; - filler = ""; strbuf_addstr(sb, "From: "); add_rfc2047(sb, line, display_name_length, encoding); strbuf_add(sb, name_tail, namelen - display_name_length); @@ -162,7 +160,7 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, } else { strbuf_addf(sb, "%s: %.*s%.*s\n", what, (fmt == CMIT_FMT_FULLER) ? 4 : 0, - filler, namelen, line); + " ", namelen, line); } switch (fmt) { case CMIT_FMT_MEDIUM: diff --git a/read-cache.c b/read-cache.c index 940ec76fd..3f587110c 100644 --- a/read-cache.c +++ b/read-cache.c @@ -67,8 +67,10 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n */ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st) { - ce->ce_ctime = st->st_ctime; - ce->ce_mtime = st->st_mtime; + ce->ce_ctime.sec = (unsigned int)st->st_ctime; + ce->ce_mtime.sec = (unsigned int)st->st_mtime; + ce->ce_ctime.nsec = ST_CTIME_NSEC(*st); + ce->ce_mtime.nsec = ST_MTIME_NSEC(*st); ce->ce_dev = st->st_dev; ce->ce_ino = st->st_ino; ce->ce_uid = st->st_uid; @@ -196,11 +198,18 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) default: die("internal error: ce_mode is %o", ce->ce_mode); } - if (ce->ce_mtime != (unsigned int) st->st_mtime) + if (ce->ce_mtime.sec != (unsigned int)st->st_mtime) changed |= MTIME_CHANGED; - if (trust_ctime && ce->ce_ctime != (unsigned int) st->st_ctime) + if (trust_ctime && ce->ce_ctime.sec != (unsigned int)st->st_ctime) changed |= CTIME_CHANGED; +#ifdef USE_NSEC + if (ce->ce_mtime.nsec != ST_MTIME_NSEC(*st)) + changed |= MTIME_CHANGED; + if (trust_ctime && ce->ce_ctime.nsec != ST_CTIME_NSEC(*st)) + changed |= CTIME_CHANGED; +#endif + if (ce->ce_uid != (unsigned int) st->st_uid || ce->ce_gid != (unsigned int) st->st_gid) changed |= OWNER_CHANGED; @@ -232,8 +241,16 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st) static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce) { return (!S_ISGITLINK(ce->ce_mode) && - istate->timestamp && - ((unsigned int)istate->timestamp) <= ce->ce_mtime); + istate->timestamp.sec && +#ifdef USE_NSEC + /* nanosecond timestamped files can also be racy! */ + (istate->timestamp.sec < ce->ce_mtime.sec || + (istate->timestamp.sec == ce->ce_mtime.sec && + istate->timestamp.nsec <= ce->ce_mtime.nsec)) +#else + istate->timestamp.sec <= ce->ce_mtime.sec +#endif + ); } int ie_match_stat(const struct index_state *istate, @@ -443,6 +460,26 @@ int remove_index_entry_at(struct index_state *istate, int pos) return 1; } +/* + * Remove all cache ententries marked for removal, that is where + * CE_REMOVE is set in ce_flags. This is much more effective than + * calling remove_index_entry_at() for each entry to be removed. + */ +void remove_marked_cache_entries(struct index_state *istate) +{ + struct cache_entry **ce_array = istate->cache; + unsigned int i, j; + + for (i = j = 0; i < istate->cache_nr; i++) { + if (ce_array[i]->ce_flags & CE_REMOVE) + remove_name_hash(ce_array[i]); + else + ce_array[j++] = ce_array[i]; + } + istate->cache_changed = 1; + istate->cache_nr = j; +} + int remove_file_from_index(struct index_state *istate, const char *path) { int pos = index_name_pos(istate, path, strlen(path)); @@ -1139,8 +1176,10 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en size_t len; const char *name; - ce->ce_ctime = ntohl(ondisk->ctime.sec); - ce->ce_mtime = ntohl(ondisk->mtime.sec); + ce->ce_ctime.sec = ntohl(ondisk->ctime.sec); + ce->ce_mtime.sec = ntohl(ondisk->mtime.sec); + ce->ce_ctime.nsec = ntohl(ondisk->ctime.nsec); + ce->ce_mtime.nsec = ntohl(ondisk->mtime.nsec); ce->ce_dev = ntohl(ondisk->dev); ce->ce_ino = ntohl(ondisk->ino); ce->ce_mode = ntohl(ondisk->mode); @@ -1206,7 +1245,8 @@ int read_index_from(struct index_state *istate, const char *path) return istate->cache_nr; errno = ENOENT; - istate->timestamp = 0; + istate->timestamp.sec = 0; + istate->timestamp.nsec = 0; fd = open(path, O_RDONLY); if (fd < 0) { if (errno == ENOENT) @@ -1258,7 +1298,9 @@ int read_index_from(struct index_state *istate, const char *path) src_offset += ondisk_ce_size(ce); dst_offset += ce_size(ce); } - istate->timestamp = st.st_mtime; + istate->timestamp.sec = st.st_mtime; + istate->timestamp.nsec = ST_MTIME_NSEC(st); + while (src_offset <= mmap_size - 20 - 8) { /* After an array of active_nr index entries, * there can be arbitrary number of extended @@ -1288,14 +1330,15 @@ unmap: int is_index_unborn(struct index_state *istate) { - return (!istate->cache_nr && !istate->alloc && !istate->timestamp); + return (!istate->cache_nr && !istate->alloc && !istate->timestamp.sec); } int discard_index(struct index_state *istate) { istate->cache_nr = 0; istate->cache_changed = 0; - istate->timestamp = 0; + istate->timestamp.sec = 0; + istate->timestamp.nsec = 0; istate->name_hash_initialized = 0; free_hash(&istate->name_hash); cache_tree_free(&(istate->cache_tree)); @@ -1441,10 +1484,10 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce) struct ondisk_cache_entry *ondisk = xcalloc(1, size); char *name; - ondisk->ctime.sec = htonl(ce->ce_ctime); - ondisk->ctime.nsec = 0; - ondisk->mtime.sec = htonl(ce->ce_mtime); - ondisk->mtime.nsec = 0; + ondisk->ctime.sec = htonl(ce->ce_ctime.sec); + ondisk->mtime.sec = htonl(ce->ce_mtime.sec); + ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec); + ondisk->mtime.nsec = htonl(ce->ce_mtime.nsec); ondisk->dev = htonl(ce->ce_dev); ondisk->ino = htonl(ce->ce_ino); ondisk->mode = htonl(ce->ce_mode); @@ -1466,13 +1509,14 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce) return ce_write(c, fd, ondisk, size); } -int write_index(const struct index_state *istate, int newfd) +int write_index(struct index_state *istate, int newfd) { git_SHA_CTX c; struct cache_header hdr; int i, err, removed, extended; struct cache_entry **cache = istate->cache; int entries = istate->cache_nr; + struct stat st; for (i = removed = extended = 0; i < entries; i++) { if (cache[i]->ce_flags & CE_REMOVE) @@ -1516,7 +1560,12 @@ int write_index(const struct index_state *istate, int newfd) if (err) return -1; } - return ce_flush(&c, newfd); + + if (ce_flush(&c, newfd) || fstat(newfd, &st)) + return -1; + istate->timestamp.sec = (unsigned int)st.st_mtime; + istate->timestamp.nsec = ST_MTIME_NSEC(st); + return 0; } /* @@ -694,6 +694,7 @@ static inline int bad_ref_char(int ch) int check_ref_format(const char *ref) { int ch, level, bad_type; + int ret = CHECK_REF_FORMAT_OK; const char *cp = ref; level = 0; @@ -709,18 +710,18 @@ int check_ref_format(const char *ref) return CHECK_REF_FORMAT_ERROR; bad_type = bad_ref_char(ch); if (bad_type) { - return (bad_type == 2 && !*cp) - ? CHECK_REF_FORMAT_WILDCARD - : CHECK_REF_FORMAT_ERROR; + if (bad_type == 2 && (!*cp || *cp == '/') && + ret == CHECK_REF_FORMAT_OK) + ret = CHECK_REF_FORMAT_WILDCARD; + else + return CHECK_REF_FORMAT_ERROR; } /* scan the rest of the path component */ while ((ch = *cp++) != 0) { bad_type = bad_ref_char(ch); if (bad_type) { - return (bad_type == 2 && !*cp) - ? CHECK_REF_FORMAT_WILDCARD - : CHECK_REF_FORMAT_ERROR; + return CHECK_REF_FORMAT_ERROR; } if (ch == '/') break; @@ -731,7 +732,7 @@ int check_ref_format(const char *ref) if (!ch) { if (level < 2) return CHECK_REF_FORMAT_ONELEVEL; - return CHECK_REF_FORMAT_OK; + return ret; } } } @@ -1628,10 +1629,10 @@ int update_ref(const char *action, const char *refname, return 0; } -struct ref *find_ref_by_name(struct ref *list, const char *name) +struct ref *find_ref_by_name(const struct ref *list, const char *name) { for ( ; list; list = list->next) if (!strcmp(list->name, name)) - return list; + return (struct ref *)list; return NULL; } @@ -5,13 +5,14 @@ #include "diff.h" #include "revision.h" #include "dir.h" +#include "tag.h" static struct refspec s_tag_refspec = { 0, 1, 0, - "refs/tags/", - "refs/tags/" + "refs/tags/*", + "refs/tags/*" }; const struct refspec *tag_refspec = &s_tag_refspec; @@ -38,6 +39,7 @@ static int branches_nr; static struct branch *current_branch; static const char *default_remote_name; +static int explicit_default_remote_name; static struct rewrite **rewrite; static int rewrite_alloc; @@ -330,8 +332,10 @@ static int handle_config(const char *key, const char *value, void *cb) if (!value) return config_error_nonbool(key); branch->remote_name = xstrdup(value); - if (branch == current_branch) + if (branch == current_branch) { default_remote_name = branch->remote_name; + explicit_default_remote_name = 1; + } } else if (!strcmp(subkey, ".merge")) { if (!value) return config_error_nonbool(key); @@ -451,16 +455,11 @@ static void read_config(void) */ static int verify_refname(char *name, int is_glob) { - int result, len = -1; + int result; - if (is_glob) { - len = strlen(name); - assert(name[len - 1] == '/'); - name[len - 1] = '\0'; - } result = check_ref_format(name); - if (is_glob) - name[len - 1] = '/'; + if (is_glob && result == CHECK_REF_FORMAT_WILDCARD) + result = CHECK_REF_FORMAT_OK; return result; } @@ -495,7 +494,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp int is_glob; const char *lhs, *rhs; - llen = is_glob = 0; + is_glob = 0; lhs = refspec[i]; if (*lhs == '+') { @@ -516,16 +515,15 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp if (rhs) { size_t rlen = strlen(++rhs); - is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*")); - rs[i].dst = xstrndup(rhs, rlen - is_glob); + is_glob = (1 <= rlen && strchr(rhs, '*')); + rs[i].dst = xstrndup(rhs, rlen); } llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); - if (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)) { + if (1 <= llen && memchr(lhs, '*', llen)) { if ((rhs && !is_glob) || (!rhs && fetch)) goto invalid; is_glob = 1; - llen--; } else if (rhs && is_glob) { goto invalid; } @@ -643,10 +641,16 @@ static int valid_remote_nick(const char *name) struct remote *remote_get(const char *name) { struct remote *ret; + int name_given = 0; read_config(); - if (!name) + if (name) + name_given = 1; + else { name = default_remote_name; + name_given = explicit_default_remote_name; + } + ret = make_remote(name, 0); if (valid_remote_nick(name)) { if (!ret->url) @@ -654,7 +658,7 @@ struct remote *remote_get(const char *name) if (!ret->url) read_branches_file(ret); } - if (!ret->url) + if (name_given && !ret->url) add_url_alias(ret, name); if (!ret->url) return NULL; @@ -719,6 +723,41 @@ int remote_has_url(struct remote *remote, const char *url) return 0; } +static int match_name_with_pattern(const char *key, const char *name, + const char *value, char **result) +{ + const char *kstar = strchr(key, '*'); + size_t klen; + size_t ksuffixlen; + size_t namelen; + int ret; + if (!kstar) + die("Key '%s' of pattern had no '*'", key); + klen = kstar - key; + ksuffixlen = strlen(kstar + 1); + namelen = strlen(name); + ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen && + !memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen); + if (ret && value) { + const char *vstar = strchr(value, '*'); + size_t vlen; + size_t vsuffixlen; + if (!vstar) + die("Value '%s' of pattern has no '*'", value); + vlen = vstar - value; + vsuffixlen = strlen(vstar + 1); + *result = xmalloc(vlen + vsuffixlen + + strlen(name) - + klen - ksuffixlen + 1); + strncpy(*result, value, vlen); + strncpy(*result + vlen, + name + klen, namelen - klen - ksuffixlen); + strcpy(*result + vlen + namelen - klen - ksuffixlen, + vstar + 1); + } + return ret; +} + int remote_find_tracking(struct remote *remote, struct refspec *refspec) { int find_src = refspec->src == NULL; @@ -742,13 +781,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec) if (!fetch->dst) continue; if (fetch->pattern) { - if (!prefixcmp(needle, key)) { - *result = xmalloc(strlen(value) + - strlen(needle) - - strlen(key) + 1); - strcpy(*result, value); - strcpy(*result + strlen(value), - needle + strlen(key)); + if (match_name_with_pattern(key, needle, value, result)) { refspec->force = fetch->force; return 0; } @@ -778,10 +811,18 @@ struct ref *alloc_ref(const char *name) static struct ref *copy_ref(const struct ref *ref) { - struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1); - memcpy(ret, ref, sizeof(struct ref) + strlen(ref->name) + 1); - ret->next = NULL; - return ret; + struct ref *cpy; + size_t len; + if (!ref) + return NULL; + len = strlen(ref->name); + cpy = xmalloc(sizeof(struct ref) + len + 1); + memcpy(cpy, ref, sizeof(struct ref) + len + 1); + cpy->next = NULL; + cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL; + cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL; + cpy->peer_ref = copy_ref(ref->peer_ref); + return cpy; } struct ref *copy_ref_list(const struct ref *ref) @@ -800,6 +841,7 @@ static void free_ref(struct ref *ref) { if (!ref) return; + free_ref(ref->peer_ref); free(ref->remote_status); free(ref->symref); free(ref); @@ -810,7 +852,6 @@ void free_refs(struct ref *ref) struct ref *next; while (ref) { next = ref->next; - free(ref->peer_ref); free_ref(ref); ref = next; } @@ -927,6 +968,7 @@ static int match_explicit(struct ref *src, struct ref *dst, struct refspec *rs) { struct ref *matched_src, *matched_dst; + int copy_src; const char *dst_value = rs->dst; char *dst_guess; @@ -937,6 +979,7 @@ static int match_explicit(struct ref *src, struct ref *dst, matched_src = matched_dst = NULL; switch (count_refspec_match(rs->src, src, &matched_src)) { case 1: + copy_src = 1; break; case 0: /* The source could be in the get_sha1() format @@ -946,6 +989,7 @@ static int match_explicit(struct ref *src, struct ref *dst, matched_src = try_explicit_object_name(rs->src); if (!matched_src) return error("src refspec %s does not match any.", rs->src); + copy_src = 0; break; default: return error("src refspec %s matches more than one.", rs->src); @@ -991,7 +1035,7 @@ static int match_explicit(struct ref *src, struct ref *dst, return error("dst ref %s receives from more than one src.", matched_dst->name); else { - matched_dst->peer_ref = matched_src; + matched_dst->peer_ref = copy_src ? copy_ref(matched_src) : matched_src; matched_dst->force = rs->force; } return 0; @@ -1020,7 +1064,8 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, continue; } - if (rs[i].pattern && !prefixcmp(src->name, rs[i].src)) + if (rs[i].pattern && match_name_with_pattern(rs[i].src, src->name, + NULL, NULL)) return rs + i; } if (matching_refs != -1) @@ -1040,6 +1085,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, struct refspec *rs; int send_all = flags & MATCH_REFS_ALL; int send_mirror = flags & MATCH_REFS_MIRROR; + int errs; static const char *default_refspec[] = { ":", 0 }; if (!nr_refspec) { @@ -1047,8 +1093,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, refspec = default_refspec; } rs = parse_push_refspec(nr_refspec, (const char **) refspec); - if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec)) - return -1; + errs = match_explicit_refs(src, dst, dst_tail, rs, nr_refspec); /* pick the remainder */ for ( ; src; src = src->next) { @@ -1074,11 +1119,9 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, } else { const char *dst_side = pat->dst ? pat->dst : pat->src; - dst_name = xmalloc(strlen(dst_side) + - strlen(src->name) - - strlen(pat->src) + 2); - strcpy(dst_name, dst_side); - strcat(dst_name, src->name + strlen(pat->src)); + if (!match_name_with_pattern(pat->src, src->name, + dst_side, &dst_name)) + die("Didn't think it matches any more"); } dst_peer = find_ref_by_name(dst, dst_name); if (dst_peer) { @@ -1099,11 +1142,13 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, dst_peer = make_linked_ref(dst_name, dst_tail); hashcpy(dst_peer->new_sha1, src->new_sha1); } - dst_peer->peer_ref = src; + dst_peer->peer_ref = copy_ref(src); dst_peer->force = pat->force; free_name: free(dst_name); } + if (errs) + return -1; return 0; } @@ -1154,19 +1199,17 @@ static struct ref *get_expanded_map(const struct ref *remote_refs, struct ref *ret = NULL; struct ref **tail = &ret; - int remote_prefix_len = strlen(refspec->src); - int local_prefix_len = strlen(refspec->dst); + char *expn_name; for (ref = remote_refs; ref; ref = ref->next) { if (strchr(ref->name, '^')) continue; /* a dereference item */ - if (!prefixcmp(ref->name, refspec->src)) { - const char *match; + if (match_name_with_pattern(refspec->src, ref->name, + refspec->dst, &expn_name)) { struct ref *cpy = copy_ref(ref); - match = ref->name + remote_prefix_len; - cpy->peer_ref = alloc_ref_with_prefix(refspec->dst, - local_prefix_len, match); + cpy->peer_ref = alloc_ref(expn_name); + free(expn_name); if (refspec->force) cpy->peer_ref->force = 1; *tail = cpy; @@ -1269,6 +1312,54 @@ int resolve_remote_symref(struct ref *ref, struct ref *list) return 1; } +static void unmark_and_free(struct commit_list *list, unsigned int mark) +{ + while (list) { + struct commit_list *temp = list; + temp->item->object.flags &= ~mark; + list = temp->next; + free(temp); + } +} + +int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1) +{ + struct object *o; + struct commit *old, *new; + struct commit_list *list, *used; + int found = 0; + + /* Both new and old must be commit-ish and new is descendant of + * old. Otherwise we require --force. + */ + o = deref_tag(parse_object(old_sha1), NULL, 0); + if (!o || o->type != OBJ_COMMIT) + return 0; + old = (struct commit *) o; + + o = deref_tag(parse_object(new_sha1), NULL, 0); + if (!o || o->type != OBJ_COMMIT) + return 0; + new = (struct commit *) o; + + if (parse_commit(new) < 0) + return 0; + + used = list = NULL; + commit_list_insert(new, &list); + while (list) { + new = pop_most_recent_commit(&list, TMP_MARK); + commit_list_insert(new, &used); + if (new == old) { + found = 1; + break; + } + } + unmark_and_free(list, TMP_MARK); + unmark_and_free(used, TMP_MARK); + return found; +} + /* * Return true if there is anything to report, otherwise false. */ @@ -1376,3 +1467,68 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb) base, num_ours, num_theirs); return 1; } + +static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) +{ + struct ref ***local_tail = cb_data; + struct ref *ref; + int len; + + /* we already know it starts with refs/ to get here */ + if (check_ref_format(refname + 5)) + return 0; + + len = strlen(refname) + 1; + ref = xcalloc(1, sizeof(*ref) + len); + hashcpy(ref->new_sha1, sha1); + memcpy(ref->name, refname, len); + **local_tail = ref; + *local_tail = &ref->next; + return 0; +} + +struct ref *get_local_heads(void) +{ + struct ref *local_refs, **local_tail = &local_refs; + for_each_ref(one_local_ref, &local_tail); + return local_refs; +} + +struct ref *guess_remote_head(const struct ref *head, + const struct ref *refs, + int all) +{ + const struct ref *r; + struct ref *list = NULL; + struct ref **tail = &list; + + if (!head) + return NULL; + + /* + * Some transports support directly peeking at + * where HEAD points; if that is the case, then + * we don't have to guess. + */ + if (head->symref) + return copy_ref(find_ref_by_name(refs, head->symref)); + + /* If refs/heads/master could be right, it is. */ + if (!all) { + r = find_ref_by_name(refs, "refs/heads/master"); + if (r && !hashcmp(r->old_sha1, head->old_sha1)) + return copy_ref(r); + } + + /* Look for another ref that points there */ + for (r = refs; r; r = r->next) { + if (r != head && !hashcmp(r->old_sha1, head->old_sha1)) { + *tail = copy_ref(r); + tail = &((*tail)->next); + if (!all) + break; + } + } + + return list; +} @@ -74,6 +74,7 @@ int check_ref_type(const struct ref *ref, int flags); void free_refs(struct ref *ref); int resolve_remote_symref(struct ref *ref, struct ref *list); +int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1); /* * Removes and frees any duplicate refs in the map. @@ -137,4 +138,15 @@ enum match_refs_flags { int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs); int format_tracking_info(struct branch *branch, struct strbuf *sb); +struct ref *get_local_heads(void); +/* + * Find refs from a list which are likely to be pointed to by the given HEAD + * ref. If 'all' is false, returns the most likely ref; otherwise, returns a + * list of all candidate refs. If no match is found (or 'head' is NULL), + * returns NULL. All returns are newly allocated and should be freed. + */ +struct ref *guess_remote_head(const struct ref *head, + const struct ref *refs, + int all); + #endif diff --git a/sideband.c b/sideband.c index cca336054..899b1ff36 100644 --- a/sideband.c +++ b/sideband.c @@ -19,7 +19,7 @@ #define FIX_SIZE 10 /* large enough for any of the above */ -int recv_sideband(const char *me, int in_stream, int out, int err) +int recv_sideband(const char *me, int in_stream, int out) { unsigned pf = strlen(PREFIX); unsigned sf; @@ -41,8 +41,7 @@ int recv_sideband(const char *me, int in_stream, int out, int err) if (len == 0) break; if (len < 1) { - len = sprintf(buf, "%s: protocol error: no band designator\n", me); - safe_write(err, buf, len); + fprintf(stderr, "%s: protocol error: no band designator\n", me); return SIDEBAND_PROTOCOL_ERROR; } band = buf[pf] & 0xff; @@ -50,8 +49,8 @@ int recv_sideband(const char *me, int in_stream, int out, int err) switch (band) { case 3: buf[pf] = ' '; - buf[pf+1+len] = '\n'; - safe_write(err, buf, pf+1+len+1); + buf[pf+1+len] = '\0'; + fprintf(stderr, "%s\n", buf); return SIDEBAND_REMOTE_ERROR; case 2: buf[pf] = ' '; @@ -95,12 +94,12 @@ int recv_sideband(const char *me, int in_stream, int out, int err) memcpy(save, b + brk, sf); b[brk + sf - 1] = b[brk - 1]; memcpy(b + brk - 1, suffix, sf); - safe_write(err, b, brk + sf); + fprintf(stderr, "%.*s", brk + sf, b); memcpy(b + brk, save, sf); len -= brk; } else { int l = brk ? brk : len; - safe_write(err, b, l); + fprintf(stderr, "%.*s", l, b); len -= l; } @@ -112,10 +111,8 @@ int recv_sideband(const char *me, int in_stream, int out, int err) safe_write(out, buf + pf+1, len); continue; default: - len = sprintf(buf, - "%s: protocol error: bad band #%d\n", - me, band); - safe_write(err, buf, len); + fprintf(stderr, "%s: protocol error: bad band #%d\n", + me, band); return SIDEBAND_PROTOCOL_ERROR; } } diff --git a/sideband.h b/sideband.h index a84b6917c..d72db35d1 100644 --- a/sideband.h +++ b/sideband.h @@ -7,7 +7,7 @@ #define DEFAULT_PACKET_MAX 1000 #define LARGE_PACKET_MAX 65520 -int recv_sideband(const char *me, int in_stream, int out, int err); +int recv_sideband(const char *me, int in_stream, int out); ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max); #endif @@ -139,14 +139,11 @@ void strbuf_list_free(struct strbuf **sbs) int strbuf_cmp(const struct strbuf *a, const struct strbuf *b) { - int cmp; - if (a->len < b->len) { - cmp = memcmp(a->buf, b->buf, a->len); - return cmp ? cmp : -1; - } else { - cmp = memcmp(a->buf, b->buf, b->len); - return cmp ? cmp : a->len != b->len; - } + int len = a->len < b->len ? a->len: b->len; + int cmp = memcmp(a->buf, b->buf, len); + if (cmp) + return cmp; + return a->len < b->len ? -1: a->len != b->len; } void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, diff --git a/string-list.c b/string-list.c index 15e14cf47..1ac536e63 100644 --- a/string-list.c +++ b/string-list.c @@ -92,6 +92,16 @@ struct string_list_item *string_list_lookup(const char *string, struct string_li return list->items + i; } +int for_each_string_list(string_list_each_func_t fn, + struct string_list *list, void *cb_data) +{ + int i, ret = 0; + for (i = 0; i < list->nr; i++) + if ((ret = fn(&list->items[i], cb_data))) + break; + return ret; +} + void string_list_clear(struct string_list *list, int free_util) { if (list->items) { diff --git a/string-list.h b/string-list.h index d32ba0520..14bbc477d 100644 --- a/string-list.h +++ b/string-list.h @@ -20,6 +20,11 @@ void string_list_clear(struct string_list *list, int free_util); typedef void (*string_list_clear_func_t)(void *p, const char *str); void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc); +/* Use this function to iterate over each item */ +typedef int (*string_list_each_func_t)(struct string_list_item *, void *); +int for_each_string_list(string_list_each_func_t, + struct string_list *list, void *cb_data); + /* Use these functions only on sorted lists: */ int string_list_has_string(const struct string_list *list, const char *string); int string_list_find_insert_index(const struct string_list *list, const char *string, diff --git a/symlinks.c b/symlinks.c index f262b7c44..1d6b35b85 100644 --- a/symlinks.c +++ b/symlinks.c @@ -1,51 +1,54 @@ #include "cache.h" -static struct cache_def { - char path[PATH_MAX + 1]; - int len; - int flags; - int track_flags; - int prefix_len_stat_func; -} cache; - /* * Returns the length (on a path component basis) of the longest - * common prefix match of 'name' and the cached path string. + * common prefix match of 'name_a' and 'name_b'. */ -static inline int longest_match_lstat_cache(int len, const char *name, - int *previous_slash) +static int longest_path_match(const char *name_a, int len_a, + const char *name_b, int len_b, + int *previous_slash) { int max_len, match_len = 0, match_len_prev = 0, i = 0; - max_len = len < cache.len ? len : cache.len; - while (i < max_len && name[i] == cache.path[i]) { - if (name[i] == '/') { + max_len = len_a < len_b ? len_a : len_b; + while (i < max_len && name_a[i] == name_b[i]) { + if (name_a[i] == '/') { match_len_prev = match_len; match_len = i; } i++; } - /* Is the cached path string a substring of 'name'? */ - if (i == cache.len && cache.len < len && name[cache.len] == '/') { - match_len_prev = match_len; - match_len = cache.len; - /* Is 'name' a substring of the cached path string? */ - } else if ((i == len && len < cache.len && cache.path[len] == '/') || - (i == len && len == cache.len)) { + /* + * Is 'name_b' a substring of 'name_a', the other way around, + * or is 'name_a' and 'name_b' the exact same string? + */ + if (i >= max_len && ((len_a > len_b && name_a[len_b] == '/') || + (len_a < len_b && name_b[len_a] == '/') || + (len_a == len_b))) { match_len_prev = match_len; - match_len = len; + match_len = i; } *previous_slash = match_len_prev; return match_len; } -static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func) +static struct cache_def { + char path[PATH_MAX + 1]; + int len; + int flags; + int track_flags; + int prefix_len_stat_func; +} cache; + +static inline void reset_lstat_cache(void) { cache.path[0] = '\0'; cache.len = 0; cache.flags = 0; - cache.track_flags = track_flags; - cache.prefix_len_stat_func = prefix_len_stat_func; + /* + * The track_flags and prefix_len_stat_func members is only + * set by the safeguard rule inside lstat_cache() + */ } #define FL_DIR (1 << 0) @@ -67,7 +70,7 @@ static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func) * of the prefix, where the cache should use the stat() function * instead of the lstat() function to test each path component. */ -static int lstat_cache(int len, const char *name, +static int lstat_cache(const char *name, int len, int track_flags, int prefix_len_stat_func) { int match_len, last_slash, last_slash_dir, previous_slash; @@ -77,11 +80,13 @@ static int lstat_cache(int len, const char *name, if (cache.track_flags != track_flags || cache.prefix_len_stat_func != prefix_len_stat_func) { /* - * As a safeguard we clear the cache if the values of - * track_flags and/or prefix_len_stat_func does not - * match with the last supplied values. + * As a safeguard rule we clear the cache if the + * values of track_flags and/or prefix_len_stat_func + * does not match with the last supplied values. */ - reset_lstat_cache(track_flags, prefix_len_stat_func); + reset_lstat_cache(); + cache.track_flags = track_flags; + cache.prefix_len_stat_func = prefix_len_stat_func; match_len = last_slash = 0; } else { /* @@ -89,7 +94,8 @@ static int lstat_cache(int len, const char *name, * the 2 "excluding" path types. */ match_len = last_slash = - longest_match_lstat_cache(len, name, &previous_slash); + longest_path_match(name, len, cache.path, cache.len, + &previous_slash); match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK); if (match_flags && match_len == cache.len) return match_flags; @@ -153,7 +159,7 @@ static int lstat_cache(int len, const char *name, cache.path[last_slash] = '\0'; cache.len = last_slash; cache.flags = save_flags; - } else if (track_flags & FL_DIR && + } else if ((track_flags & FL_DIR) && last_slash_dir > 0 && last_slash_dir <= PATH_MAX) { /* * We have a separate test for the directory case, @@ -170,7 +176,7 @@ static int lstat_cache(int len, const char *name, cache.len = last_slash_dir; cache.flags = FL_DIR; } else { - reset_lstat_cache(track_flags, prefix_len_stat_func); + reset_lstat_cache(); } return ret_flags; } @@ -179,19 +185,19 @@ static int lstat_cache(int len, const char *name, * Invalidate the given 'name' from the cache, if 'name' matches * completely with the cache. */ -void invalidate_lstat_cache(int len, const char *name) +void invalidate_lstat_cache(const char *name, int len) { int match_len, previous_slash; - match_len = longest_match_lstat_cache(len, name, &previous_slash); + match_len = longest_path_match(name, len, cache.path, cache.len, + &previous_slash); if (len == match_len) { if ((cache.track_flags & FL_DIR) && previous_slash > 0) { cache.path[previous_slash] = '\0'; cache.len = previous_slash; cache.flags = FL_DIR; } else - reset_lstat_cache(cache.track_flags, - cache.prefix_len_stat_func); + reset_lstat_cache(); } } @@ -200,7 +206,7 @@ void invalidate_lstat_cache(int len, const char *name) */ void clear_lstat_cache(void) { - reset_lstat_cache(0, 0); + reset_lstat_cache(); } #define USE_ONLY_LSTAT 0 @@ -208,9 +214,9 @@ void clear_lstat_cache(void) /* * Return non-zero if path 'name' has a leading symlink component */ -int has_symlink_leading_path(int len, const char *name) +int has_symlink_leading_path(const char *name, int len) { - return lstat_cache(len, name, + return lstat_cache(name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK; } @@ -219,9 +225,9 @@ int has_symlink_leading_path(int len, const char *name) * Return non-zero if path 'name' has a leading symlink component or * if some leading path component does not exists. */ -int has_symlink_or_noent_leading_path(int len, const char *name) +int has_symlink_or_noent_leading_path(const char *name, int len) { - return lstat_cache(len, name, + return lstat_cache(name, len, FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) & (FL_SYMLINK|FL_NOENT); } @@ -233,9 +239,68 @@ int has_symlink_or_noent_leading_path(int len, const char *name) * 'prefix_len', thus we then allow for symlinks in the prefix part as * long as those points to real existing directories. */ -int has_dirs_only_path(int len, const char *name, int prefix_len) +int has_dirs_only_path(const char *name, int len, int prefix_len) { - return lstat_cache(len, name, + return lstat_cache(name, len, FL_DIR|FL_FULLPATH, prefix_len) & FL_DIR; } + +static struct removal_def { + char path[PATH_MAX]; + int len; +} removal; + +static void do_remove_scheduled_dirs(int new_len) +{ + while (removal.len > new_len) { + removal.path[removal.len] = '\0'; + if (rmdir(removal.path)) + break; + do { + removal.len--; + } while (removal.len > new_len && + removal.path[removal.len] != '/'); + } + removal.len = new_len; + return; +} + +void schedule_dir_for_removal(const char *name, int len) +{ + int match_len, last_slash, i, previous_slash; + + match_len = last_slash = i = + longest_path_match(name, len, removal.path, removal.len, + &previous_slash); + /* Find last slash inside 'name' */ + while (i < len) { + if (name[i] == '/') + last_slash = i; + i++; + } + + /* + * If we are about to go down the directory tree, we check if + * we must first go upwards the tree, such that we then can + * remove possible empty directories as we go upwards. + */ + if (match_len < last_slash && match_len < removal.len) + do_remove_scheduled_dirs(match_len); + /* + * If we go deeper down the directory tree, we only need to + * save the new path components as we go down. + */ + if (match_len < last_slash) { + memcpy(&removal.path[match_len], &name[match_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 67c431d4e..de384e6ac 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -5,7 +5,7 @@ git_svn_id=git""-svn-id if test -n "$NO_SVN_TESTS" then - test_expect_success 'skipping git svn tests, NO_SVN_TESTS defined' : + say 'skipping git svn tests, NO_SVN_TESTS defined' test_done exit fi @@ -17,7 +17,7 @@ SVN_TREE=$GIT_SVN_DIR/svn-tree svn >/dev/null 2>&1 if test $? -ne 1 then - test_expect_success 'skipping git svn tests, svn not found' : + say 'skipping git svn tests, svn not found' test_done exit fi @@ -41,7 +41,7 @@ then else err='Perl SVN libraries not found or unusable, skipping test' fi - test_expect_success "$err" : + say "$err" test_done exit fi diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index 86cdebc72..589aaf821 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -94,13 +94,18 @@ prepare_httpd() { } start_httpd() { - prepare_httpd + prepare_httpd >&3 2>&4 trap 'stop_httpd; 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 + -c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \ + >&3 2>&4 + if ! test $? = 0; then + say "skipping test, web server setup failed" + test_done + fi } stop_httpd() { diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 70df15cbd..ddcd5b0ef 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -127,7 +127,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'validate git ls-files output for a known tree.' \ - 'diff current expected' + 'test_cmp expected current' test_expect_success \ 'writing tree out with git write-tree.' \ @@ -147,7 +147,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'git ls-tree output for a known tree.' \ - 'diff current expected' + 'test_cmp expected current' # This changed in ls-tree pathspec change -- recursive does # not show tree nodes anymore. @@ -166,7 +166,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'git ls-tree -r output for a known tree.' \ - 'diff current expected' + 'test_cmp expected current' # But with -r -t we can have both. test_expect_success \ @@ -187,7 +187,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'git ls-tree -r output for a known tree.' \ - 'diff current expected' + 'test_cmp expected current' test_expect_success \ 'writing partial tree out with git write-tree --prefix.' \ diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh index e5330395f..ae90d34f6 100755 --- a/t/t0024-crlf-archive.sh +++ b/t/t0024-crlf-archive.sh @@ -28,7 +28,7 @@ test_expect_success 'tar archive' ' "$UNZIP" -v >/dev/null 2>&1 if [ $? -eq 127 ]; then - echo "Skipping ZIP test, because unzip was not found" + say "Skipping ZIP test, because unzip was not found" test_done exit fi diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh index 7edf49db3..a449580c8 100755 --- a/t/t0050-filesystem.sh +++ b/t/t0050-filesystem.sh @@ -8,6 +8,7 @@ auml=`printf '\xc3\xa4'` aumlcdiar=`printf '\x61\xcc\x88'` case_insensitive= +unibad= test_expect_success 'see if we expect ' ' test_case=test_expect_success @@ -19,7 +20,6 @@ test_expect_success 'see if we expect ' ' then test_case=test_expect_failure case_insensitive=t - say "will test on a case insensitive filesystem" fi && rm -fr junk && mkdir junk && @@ -27,13 +27,18 @@ test_expect_success 'see if we expect ' ' case "$(cd junk && echo *)" in "$aumlcdiar") test_unicode=test_expect_failure - say "will test on a unicode corrupting filesystem" + unibad=t ;; *) ;; esac && rm -fr junk ' +test "$case_insensitive" && + say "will test on a case insensitive filesystem" +test "$unibad" && + say "will test on a unicode corrupting filesystem" + if test "$case_insensitive" then test_expect_success "detection of case insensitive filesystem during repo init" ' diff --git a/t/t1008-read-tree-overlay.sh b/t/t1008-read-tree-overlay.sh new file mode 100755 index 000000000..f9e00285d --- /dev/null +++ b/t/t1008-read-tree-overlay.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +test_description='test multi-tree read-tree without merging' + +. ./test-lib.sh + +test_expect_success setup ' + echo one >a && + git add a && + git commit -m initial && + git tag initial && + echo two >b && + git add b && + git commit -m second && + git checkout -b side initial && + echo three >a && + mkdir b && + echo four >b/c && + git add b/c && + git commit -m third +' + +test_expect_success 'multi-read' ' + git read-tree initial master side && + (echo a; echo b/c) >expect && + git ls-files >actual && + test_cmp expect actual +' + +test_done + diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh index 7f7fc3673..c4414ff57 100755 --- a/t/t1100-commit-tree-options.sh +++ b/t/t1100-commit-tree-options.sh @@ -40,6 +40,6 @@ test_expect_success \ test_expect_success \ 'compare commit' \ - 'diff expected commit' + 'test_cmp expected commit' test_done diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 3c06842d9..9c81e041b 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -118,7 +118,14 @@ EOF test_expect_success 'multiple unset is correct' 'cmp .git/config expect' -mv .git/config2 .git/config +cp .git/config2 .git/config + +test_expect_success '--replace-all missing value' ' + test_must_fail git config --replace-all beta.haha && + test_cmp .git/config2 .git/config +' + +rm .git/config2 test_expect_success '--replace-all' \ 'git config --replace-all beta.haha gamma' diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index bd589268f..54ba3df95 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -137,7 +137,7 @@ $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000 EOF test_expect_success \ "verifying $m's log" \ - "diff expect .git/logs/$m" + "test_cmp expect .git/logs/$m" rm -rf .git/$m .git/logs expect test_expect_success \ @@ -168,7 +168,7 @@ $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000 EOF test_expect_success \ "verifying $m's log" \ - 'diff expect .git/logs/$m' + 'test_cmp expect .git/logs/$m' rm -f .git/$m .git/logs/$m expect git update-ref $m $D @@ -272,7 +272,7 @@ $h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000 c EOF test_expect_success \ 'git commit logged updates' \ - "diff expect .git/logs/$m" + "test_cmp expect .git/logs/$m" unset h_TEST h_OTHER h_FIXED h_MERGED test_expect_success \ diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh index 5b24f0557..80af6b9b7 100755 --- a/t/t1410-reflog.sh +++ b/t/t1410-reflog.sh @@ -70,9 +70,7 @@ test_expect_success setup ' E=`git rev-parse --verify HEAD:A/B/E` && check_fsck && - chmod +x C && - ( test "`git config --bool core.filemode`" != false || - echo executable >>C ) && + test_chmod +x C && git add C && test_tick && git commit -m dragon && L=`git rev-parse --verify HEAD` && diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index b2ddf5ace..5a8d52f2f 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -150,7 +150,7 @@ test_expect_success 'add -u resolves unmerged paths' ' echo 2 >path3 && echo 2 >path5 && git add -u && - git ls-files -s "path?" >actual && + git ls-files -s path1 path2 path3 path4 path5 path6 >actual && { echo "100644 $three 0 path1" echo "100644 $one 1 path3" diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh index bc0a35139..36eee0f8a 100755 --- a/t/t3000-ls-files-others.sh +++ b/t/t3000-ls-files-others.sh @@ -42,7 +42,7 @@ test_expect_success \ test_expect_success \ 'git ls-files --others should pick up symlinks.' \ - 'diff output expected1' + 'test_cmp expected1 output' test_expect_success \ 'git ls-files --others --directory to show output.' \ @@ -51,6 +51,6 @@ test_expect_success \ test_expect_success \ 'git ls-files --others --directory should not get confused.' \ - 'diff output expected2' + 'test_cmp expected2 output' test_done diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh index ec1404063..e4f02a096 100755 --- a/t/t3010-ls-files-killed-modified.sh +++ b/t/t3010-ls-files-killed-modified.sh @@ -75,7 +75,7 @@ EOF test_expect_success \ 'validate git ls-files -k output.' \ - 'diff .output .expected' + 'test_cmp .expected .output' test_expect_success \ 'git ls-files -m to show modified files.' \ @@ -91,6 +91,6 @@ EOF test_expect_success \ 'validate git ls-files -m output.' \ - 'diff .output .expected' + 'test_cmp .expected .output' test_done diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index be7ae5a00..6e391a370 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -83,9 +83,9 @@ test_expect_success 'rebase a single mode change' ' git checkout -b modechange HEAD^ && echo 1 > X && git add X && - chmod a+x A && + test_chmod +x A && test_tick && - git commit -m modechange A X && + git commit -m modechange && GIT_TRACE=1 git rebase master ' diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 603b003ed..c32ff6682 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -459,4 +459,15 @@ test_expect_success 'submodule rebase -i' ' FAKE_LINES="1 squash 2 3" git rebase -i A ' +test_expect_success 'avoid unnecessary reset' ' + git checkout master && + test-chmtime =123456789 file3 && + git update-index --refresh && + HEAD=$(git rev-parse HEAD) && + git rebase -i HEAD~4 && + test $HEAD = $(git rev-parse HEAD) && + MTIME=$(test-chmtime -v +0 file3 | sed 's/[^0-9].*$//') && + test 123456789 = $MTIME +' + test_done diff --git a/t/t3409-rebase-hook.sh b/t/t3413-rebase-hook.sh index 098b75507..098b75507 100755 --- a/t/t3409-rebase-hook.sh +++ b/t/t3413-rebase-hook.sh diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 95542e9cf..2aefbcf47 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -21,10 +21,11 @@ embedded' embedded' && git commit -m 'add files with tabs and newlines' else - say 'Your filesystem does not allow tabs in filenames.' test_tabs=n fi" +test "$test_tabs" = n && say 'Your filesystem does not allow tabs in filenames.' + # Later we will try removing an unremovable path to make sure # git rm barfs, but if the test is run as root that cannot be # arranged. @@ -112,7 +113,7 @@ test_expect_success \ 'test_must_fail git rm -f baz' chmod 775 . else - test_expect_success 'skipping removal failure (perhaps running as root?)' : + say 'skipping removal failure test (perhaps running as root?)' fi test_expect_success \ diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh index 4e92fce1d..8c1b81e24 100755 --- a/t/t4006-diff-mode.sh +++ b/t/t4006-diff-mode.sh @@ -15,21 +15,10 @@ test_expect_success \ tree=`git write-tree` && echo $tree' -if [ "$(git config --get core.filemode)" = false ] -then - say 'filemode disabled on the filesystem, using update-index --chmod=+x' - test_expect_success \ - 'git update-index --chmod=+x' \ - 'git update-index rezrov && - git update-index --chmod=+x rezrov && - git diff-index $tree >current' -else - test_expect_success \ - 'chmod' \ - 'chmod +x rezrov && - git update-index rezrov && - git diff-index $tree >current' -fi +test_expect_success \ + 'chmod' \ + 'test_chmod +x rezrov && + git diff-index $tree >current' _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" diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 9cd5a6e68..426e64e82 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -101,8 +101,7 @@ do '' | '#'*) continue ;; esac test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'` - cnt=`expr $test_count + 1` - pfx=`printf "%04d" $cnt` + pfx=`printf "%04d" $test_count` expect="$TEST_DIRECTORY/t4013/diff.$test" actual="$pfx-diff.$test" diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index ebfc4a659..f187d15e3 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -16,9 +16,7 @@ test_expect_success setup ' git checkout -b side && for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file && - chmod +x elif && - git update-index file elif && - git update-index --chmod=+x elif && + test_chmod +x elif && git commit -m "Side changes #1" && for i in D E F; do echo "$i"; done >>file && diff --git a/t/t4017-quiet.sh b/t/t4035-diff-quiet.sh index e747e8422..e747e8422 100755 --- a/t/t4017-quiet.sh +++ b/t/t4035-diff-quiet.sh diff --git a/t/t4021-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh index ba43f1854..ba43f1854 100755 --- a/t/t4021-format-patch-signer-mime.sh +++ b/t/t4036-format-patch-signer-mime.sh diff --git a/t/t4203-patch-id.sh b/t/t4204-patch-id.sh index 04f7bae85..04f7bae85 100755 --- a/t/t4203-patch-id.sh +++ b/t/t4204-patch-id.sh diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index b7e362834..7a84ab61d 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -76,7 +76,7 @@ test_expect_success \ test_expect_success \ 'git archive vs. git tar-tree' \ - 'diff b.tar b2.tar' + 'test_cmp b.tar b2.tar' test_expect_success \ 'git archive in a bare repo' \ @@ -96,12 +96,12 @@ test_expect_success \ "$TAR" xf b.tar -C extract a/a && test-chmtime -v +0 extract/a/a |cut -f 1 >b.mtime && echo "1117231200" >expected.mtime && - diff expected.mtime b.mtime' + test_cmp expected.mtime b.mtime' test_expect_success \ 'git get-tar-commit-id' \ 'git get-tar-commit-id <b.tar >b.commitid && - diff .git/$(git symbolic-ref HEAD) b.commitid' + test_cmp .git/$(git symbolic-ref HEAD) b.commitid' test_expect_success \ 'extract tar archive' \ @@ -110,7 +110,7 @@ test_expect_success \ test_expect_success \ 'validate filenames' \ '(cd b/a && find .) | sort >b.lst && - diff a.lst b.lst' + test_cmp a.lst b.lst' test_expect_success \ 'validate file contents' \ @@ -127,7 +127,7 @@ test_expect_success \ test_expect_success \ 'validate filenames with prefix' \ '(cd c/prefix/a && find .) | sort >c.lst && - diff a.lst c.lst' + test_cmp a.lst c.lst' test_expect_success \ 'validate file contents with prefix' \ @@ -148,8 +148,8 @@ test_expect_success \ 'validate substfile contents' \ 'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \ >f/a/substfile1.expected && - diff f/a/substfile1.expected f/a/substfile1 && - diff a/substfile2 f/a/substfile2 + test_cmp f/a/substfile1.expected f/a/substfile1 && + test_cmp a/substfile2 f/a/substfile2 ' test_expect_success \ @@ -160,8 +160,8 @@ test_expect_success \ 'validate substfile contents from archive with prefix' \ 'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \ >g/prefix/a/substfile1.expected && - diff g/prefix/a/substfile1.expected g/prefix/a/substfile1 && - diff a/substfile2 g/prefix/a/substfile2 + test_cmp g/prefix/a/substfile1.expected g/prefix/a/substfile1 && + test_cmp a/substfile2 g/prefix/a/substfile2 ' test_expect_success \ @@ -182,7 +182,7 @@ test_expect_success 'git archive --format=zip with --output' \ $UNZIP -v >/dev/null 2>&1 if [ $? -eq 127 ]; then - echo "Skipping ZIP tests, because unzip was not found" + say "Skipping ZIP tests, because unzip was not found" test_done exit fi @@ -194,7 +194,7 @@ test_expect_success \ test_expect_success \ 'validate filenames' \ '(cd d/a && find .) | sort >d.lst && - diff a.lst d.lst' + test_cmp a.lst d.lst' test_expect_success \ 'validate file contents' \ @@ -211,7 +211,7 @@ test_expect_success \ test_expect_success \ 'validate filenames with prefix' \ '(cd e/prefix/a && find .) | sort >e.lst && - diff a.lst e.lst' + test_cmp a.lst e.lst' test_expect_success \ 'validate file contents with prefix' \ diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index ccfc64c6e..e2aa254ea 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -13,11 +13,10 @@ TRASH=`pwd` test_expect_success \ 'setup' \ 'rm -f .git/index* - for i in a b c - do - dd if=/dev/zero bs=4k count=1 | perl -pe "y/\\000/$i/" >$i && - git update-index --add $i || return 1 - done && + perl -e "print \"a\" x 4096;" > a && + perl -e "print \"b\" x 4096;" > b && + perl -e "print \"c\" x 4096;" > c && + git update-index --add a b c && cat c >d && echo foo >>d && git update-index --add d && tree=`git write-tree` && commit=`git commit-tree $tree </dev/null` && { @@ -221,7 +220,7 @@ test_expect_success \ test_expect_success \ 'verify-pack catches a corrupted pack signature' \ 'cat test-1-${packname_1}.pack >test-3.pack && - dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=2 && + echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=2 && if git verify-pack test-3.idx then false else :; @@ -230,7 +229,7 @@ test_expect_success \ test_expect_success \ 'verify-pack catches a corrupted pack version' \ 'cat test-1-${packname_1}.pack >test-3.pack && - dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=7 && + echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=7 && if git verify-pack test-3.idx then false else :; @@ -239,7 +238,7 @@ test_expect_success \ test_expect_success \ 'verify-pack catches a corrupted type/size of the 1st packed object data' \ 'cat test-1-${packname_1}.pack >test-3.pack && - dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=12 && + echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=12 && if git verify-pack test-3.idx then false else :; @@ -250,7 +249,7 @@ test_expect_success \ 'l=`wc -c <test-3.idx` && l=`expr $l - 20` && cat test-1-${packname_1}.pack >test-3.pack && - dd if=/dev/zero of=test-3.idx count=20 bs=1 conv=notrunc seek=$l && + printf "%20s" "" | dd of=test-3.idx count=20 bs=1 conv=notrunc seek=$l && if git verify-pack test-3.pack then false else :; diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index e6f70d474..77982cdea 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh @@ -208,7 +208,7 @@ test_expect_success \ obj=`git hash-object file_001` && nr=`index_obj_nr ".git/objects/pack/pack-${pack1}.idx" $obj` && chmod +w ".git/objects/pack/pack-${pack1}.idx" && - dd if=/dev/zero of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \ + printf xxxx | dd of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \ bs=1 count=4 seek=$((8 + 256 * 4 + `wc -l <obj-list` * 20 + $nr * 4)) && ( while read obj do git cat-file -p $obj >/dev/null || exit 1 diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh index d4e30fc43..5132d4130 100755 --- a/t/t5303-pack-corruption-resilience.sh +++ b/t/t5303-pack-corruption-resilience.sh @@ -55,6 +55,8 @@ do_corrupt_object() { test_must_fail git verify-pack ${pack}.pack } +printf '\0' > zero + test_expect_success \ 'initial setup validation' \ 'create_test_files && @@ -66,7 +68,7 @@ test_expect_success \ test_expect_success \ 'create corruption in header of first object' \ - 'do_corrupt_object $blob_1 0 < /dev/zero && + 'do_corrupt_object $blob_1 0 < zero && test_must_fail git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null' @@ -125,7 +127,7 @@ test_expect_success \ 'create corruption in header of first delta' \ 'create_new_pack && git prune-packed && - do_corrupt_object $blob_2 0 < /dev/zero && + do_corrupt_object $blob_2 0 < zero && git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null' @@ -180,7 +182,7 @@ test_expect_success \ 'corruption in delta base reference of first delta (OBJ_REF_DELTA)' \ 'create_new_pack && git prune-packed && - do_corrupt_object $blob_2 2 < /dev/zero && + do_corrupt_object $blob_2 2 < zero && git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null' @@ -207,7 +209,7 @@ test_expect_success \ 'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' \ 'create_new_pack --delta-base-offset && git prune-packed && - do_corrupt_object $blob_2 2 < /dev/zero && + do_corrupt_object $blob_2 2 < zero && git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null' @@ -259,7 +261,7 @@ test_expect_success \ test_expect_success \ '... and a redundant pack allows for full recovery too' \ - 'do_corrupt_object $blob_2 2 < /dev/zero && + 'do_corrupt_object $blob_2 2 < zero && git cat-file blob $blob_1 > /dev/null && test_must_fail git cat-file blob $blob_2 > /dev/null && test_must_fail git cat-file blob $blob_3 > /dev/null && diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh index 4fdb41855..5858b868e 100755 --- a/t/t5403-post-checkout-hook.sh +++ b/t/t5403-post-checkout-hook.sh @@ -71,6 +71,7 @@ test_expect_success 'post-checkout receives the right args when not switching br test $old = $new -a $flag = 0 ' +if test "$(git config --bool core.filemode)" = true; then mkdir -p templates/hooks cat >templates/hooks/post-checkout <<'EOF' #!/bin/sh @@ -82,5 +83,6 @@ test_expect_success 'post-checkout hook is triggered by clone' ' git clone --template=templates . clone3 && test -f clone3/.git/post-checkout.args ' +fi test_done diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index eb637184a..5ec668d6d 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -28,7 +28,7 @@ tokens_match () { } check_remote_track () { - actual=$(git remote show "$1" | sed -e '1,/Tracked/d') && + actual=$(git remote show "$1" | sed -ne 's|^ \(.*\) tracked$|\1|p') shift && tokens_match "$*" "$actual" } @@ -136,47 +136,73 @@ EOF cat > test/expect << EOF * remote origin URL: $(pwd)/one - Remote branch merged with 'git pull' while on branch master + HEAD branch: master + Remote branches: + master new (next fetch will store in remotes/origin) + side tracked + Local branches configured for 'git pull': + ahead merges with remote master + master merges with remote master + octopus merges with remote topic-a + and with remote topic-b + and with remote topic-c + rebase rebases onto remote master + Local refs configured for 'git push': + master pushes to master (local out of date) + master pushes to upstream (create) +* remote two + URL: ../two + HEAD branch (remote HEAD is ambiguous, may be one of the following): + another master - New remote branch (next fetch will store in remotes/origin) - master - Tracked remote branches - side - master - Local branches pushed with 'git push' - master:upstream - +refs/tags/lastbackup + Local refs configured for 'git push': + ahead forces to master (fast forwardable) + master pushes to another (up to date) EOF test_expect_success 'show' ' (cd test && - git config --add remote.origin.fetch \ - refs/heads/master:refs/heads/upstream && + git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream && git fetch && + git checkout -b ahead origin/master && + echo 1 >> file && + test_tick && + git commit -m update file && + git checkout master && + git branch --track octopus origin/master && + git branch --track rebase origin/master && git branch -d -r origin/master && + git config --add remote.two.url ../two && + git config branch.rebase.rebase true && + git config branch.octopus.merge "topic-a topic-b topic-c" && (cd ../one && echo 1 > file && test_tick && git commit -m update file) && - git config remote.origin.push \ - refs/heads/master:refs/heads/upstream && - git config --add remote.origin.push \ - +refs/tags/lastbackup && - git remote show origin > output && + git config --add remote.origin.push : && + git config --add remote.origin.push refs/heads/master:refs/heads/upstream && + git config --add remote.origin.push +refs/tags/lastbackup && + git config --add remote.two.push +refs/heads/ahead:refs/heads/master && + git config --add remote.two.push refs/heads/master:refs/heads/another && + git remote show origin two > output && + git branch -d rebase octopus && test_cmp expect output) ' cat > test/expect << EOF * remote origin URL: $(pwd)/one - Remote branch merged with 'git pull' while on branch master - master - Tracked remote branches + HEAD branch: (not queried) + Remote branches: (status not queried) master side - Local branches pushed with 'git push' - master:upstream - +refs/tags/lastbackup + Local branches configured for 'git pull': + ahead merges with remote master + master merges with remote master + Local refs configured for 'git push' (status not queried): + (matching) pushes to (matching) + refs/heads/master pushes to refs/heads/upstream + refs/tags/lastbackup forces to refs/tags/lastbackup EOF test_expect_success 'show -n' ' @@ -197,6 +223,46 @@ test_expect_success 'prune' ' test_must_fail git rev-parse refs/remotes/origin/side) ' +test_expect_success 'set-head --delete' ' + (cd test && + git symbolic-ref refs/remotes/origin/HEAD && + git remote set-head --delete origin && + test_must_fail git symbolic-ref refs/remotes/origin/HEAD) +' + +test_expect_success 'set-head --auto' ' + (cd test && + git remote set-head --auto origin && + echo refs/remotes/origin/master >expect && + git symbolic-ref refs/remotes/origin/HEAD >output && + test_cmp expect output + ) +' + +cat >test/expect <<EOF +error: Multiple remote HEAD branches. Please choose one explicitly with: + git remote set-head two another + git remote set-head two master +EOF + +test_expect_success 'set-head --auto fails w/multiple HEADs' ' + (cd test && + test_must_fail git remote set-head --auto two >output 2>&1 && + test_cmp expect output) +' + +cat >test/expect <<EOF +refs/remotes/origin/side2 +EOF + +test_expect_success 'set-head explicit' ' + (cd test && + git remote set-head origin side2 && + git symbolic-ref refs/remotes/origin/HEAD >output && + git remote set-head origin master && + test_cmp expect output) +' + cat > test/expect << EOF Pruning origin URL: $(pwd)/one @@ -343,7 +409,7 @@ test_expect_success '"remote show" does not show symbolic refs' ' git clone one three && (cd three && git remote show origin > output && - ! grep HEAD < output && + ! grep "^ *HEAD$" < output && ! grep -i stale < output) ' diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 9e679b402..bee3424fe 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -191,38 +191,39 @@ test_expect_success 'bundle should be able to create a full history' ' ' -test "$TEST_RSYNC" && { +! rsync --help > /dev/null 2> /dev/null && +say 'Skipping rsync tests because rsync was not found' || { test_expect_success 'fetch via rsync' ' git pack-refs && mkdir rsynced && - cd rsynced && - git init && - git fetch rsync://127.0.0.1$(pwd)/../.git master:refs/heads/master && - git gc --prune && - test $(git rev-parse master) = $(cd .. && git rev-parse master) && - git fsck --full + (cd rsynced && + git init --bare && + git fetch "rsync:$(pwd)/../.git" master:refs/heads/master && + git gc --prune && + test $(git rev-parse master) = $(cd .. && git rev-parse master) && + git fsck --full) ' test_expect_success 'push via rsync' ' - mkdir ../rsynced2 && - (cd ../rsynced2 && + mkdir rsynced2 && + (cd rsynced2 && git init) && - git push rsync://127.0.0.1$(pwd)/../rsynced2/.git master && - cd ../rsynced2 && - git gc --prune && - test $(git rev-parse master) = $(cd .. && git rev-parse master) && - git fsck --full + (cd rsynced && + git push "rsync:$(pwd)/../rsynced2/.git" master) && + (cd rsynced2 && + git gc --prune && + test $(git rev-parse master) = $(cd .. && git rev-parse master) && + git fsck --full) ' test_expect_success 'push via rsync' ' - cd .. && mkdir rsynced3 && (cd rsynced3 && git init) && - git push --all rsync://127.0.0.1$(pwd)/rsynced3/.git && - cd rsynced3 && - test $(git rev-parse master) = $(cd .. && git rev-parse master) && - git fsck --full + git push --all "rsync:$(pwd)/rsynced3/.git" && + (cd rsynced3 && + test $(git rev-parse master) = $(cd .. && git rev-parse master) && + git fsck --full) ' } diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh index 22ba38003..c28932216 100755 --- a/t/t5511-refspec.sh +++ b/t/t5511-refspec.sh @@ -72,4 +72,16 @@ test_refspec fetch ':refs/remotes/frotz/HEAD-to-me' test_refspec push ':refs/remotes/frotz/delete me' invalid test_refspec fetch ':refs/remotes/frotz/HEAD to me' invalid +test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid +test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid + +test_refspec fetch 'refs/heads*/for-linus:refs/remotes/mine/*' invalid +test_refspec push 'refs/heads*/for-linus:refs/remotes/mine/*' invalid + +test_refspec fetch 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid +test_refspec push 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid + +test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*' +test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*' + test_done diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh index 1f4608d8b..dbb927dec 100755 --- a/t/t5515-fetch-merge-logic.sh +++ b/t/t5515-fetch-merge-logic.sh @@ -129,8 +129,7 @@ do '' | '#'*) continue ;; esac test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'` - cnt=`expr $test_count + 1` - pfx=`printf "%04d" $cnt` + pfx=`printf "%04d" $test_count` expect_f="$TEST_DIRECTORY/t5515/fetch.$test" actual_f="$pfx-fetch.$test" expect_r="$TEST_DIRECTORY/t5515/refs.$test" diff --git a/t/t5521-pull-symlink.sh b/t/t5522-pull-symlink.sh index 5672b51e2..5672b51e2 100755 --- a/t/t5521-pull-symlink.sh +++ b/t/t5522-pull-symlink.sh diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index 10e5fd0d5..c46592f03 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -11,6 +11,7 @@ This test runs various sanity checks on http-push.' ROOT_PATH="$PWD" LIB_HTTPD_DAV=t +LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'} if git http-push > /dev/null 2>&1 || [ $? -eq 128 ] then @@ -20,13 +21,7 @@ then fi . "$TEST_DIRECTORY"/lib-httpd.sh - -if ! start_httpd >&3 2>&4 -then - say "skipping test, web server setup failed" - test_done - exit -fi +start_httpd test_expect_success 'setup remote repository' ' cd "$ROOT_PATH" && diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh new file mode 100755 index 000000000..05b1b62cb --- /dev/null +++ b/t/t5550-http-fetch.sh @@ -0,0 +1,57 @@ +#!/bin/sh + +test_description='test fetching over http' +. ./test-lib.sh + +if test -n "$NO_CURL"; then + say 'skipping test, git built without http support' + test_done +fi + +. "$TEST_DIRECTORY"/lib-httpd.sh +LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'} +start_httpd + +test_expect_success 'setup repository' ' + echo content >file && + git add file && + git commit -m one +' + +test_expect_success 'create http-accessible bare repository' ' + mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git --bare init && + echo "exec git update-server-info" >hooks/post-update && + chmod +x hooks/post-update + ) && + git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git push public master:master +' + +test_expect_success 'clone http repository' ' + git clone $HTTPD_URL/repo.git clone && + test_cmp file clone/file +' + +test_expect_success 'fetch changes via http' ' + echo content >>file && + git commit -a -m two && + git push public + (cd clone && git pull) && + test_cmp file clone/file +' + +test_expect_success 'http remote detects correct HEAD' ' + git push public master:other && + (cd clone && + git remote set-head origin -d && + git remote set-head origin -a && + git symbolic-ref refs/remotes/origin/HEAD > output && + echo refs/remotes/origin/master > expect && + test_cmp expect output + ) +' + +stop_httpd +test_done diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 44793f2ee..2335d8bc8 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -159,4 +159,19 @@ test_expect_success 'clone a void' ' test_cmp target-6/.git/config target-7/.git/config ' +test_expect_success 'clone respects global branch.autosetuprebase' ' + ( + HOME=$(pwd) && + export HOME && + test_config="$HOME/.gitconfig" && + unset GIT_CONFIG_NOGLOBAL && + git config -f "$test_config" branch.autosetuprebase remote && + rm -fr dst && + git clone src dst && + cd dst && + actual="z$(git config branch.master.rebase)" && + test ztrue = $actual + ) +' + test_done diff --git a/t/t5602-clone-remote-exec.sh b/t/t5602-clone-remote-exec.sh index 82b1d1e2b..deffdaee4 100755 --- a/t/t5602-clone-remote-exec.sh +++ b/t/t5602-clone-remote-exec.sh @@ -18,8 +18,8 @@ test_expect_success 'clone calls git upload-pack unqualified with no -u option' ' test_expect_success 'clone calls specified git upload-pack with -u option' ' - GIT_SSH=./not_ssh git clone -u /something/bin/git-upload-pack localhost:/path/to/repo junk - echo "localhost /something/bin/git-upload-pack '\''/path/to/repo'\''" >expected + GIT_SSH=./not_ssh git clone -u ./something/bin/git-upload-pack localhost:/path/to/repo junk + echo "localhost ./something/bin/git-upload-pack '\''/path/to/repo'\''" >expected test_cmp expected not_ssh_output ' diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh index 8073e0c3e..41c6860be 100755 --- a/t/t6031-merge-recursive.sh +++ b/t/t6031-merge-recursive.sh @@ -3,9 +3,6 @@ test_description='merge-recursive: handle file mode' . ./test-lib.sh -# Note that we follow "chmod +x F" with "update-index --chmod=+x F" to -# help filesystems that do not have the executable bit. - test_expect_success 'mode change in one branch: keep changed version' ' : >file1 && git add file1 && @@ -15,8 +12,7 @@ test_expect_success 'mode change in one branch: keep changed version' ' git add dummy && git commit -m a && git checkout -b b1 master && - chmod +x file1 && - git update-index --chmod=+x file1 && + test_chmod +x file1 && git commit -m b1 && git checkout a1 && git merge-recursive master -- a1 b1 && @@ -28,8 +24,7 @@ test_expect_success 'mode change in both branches: expect conflict' ' git checkout -b a2 master && : >file2 && H=$(git hash-object file2) && - chmod +x file2 && - git update-index --add --chmod=+x file2 && + test_chmod +x file2 && git commit -m a2 && git checkout -b b2 master && : >file2 && diff --git a/t/t6023-merge-rename-nocruft.sh b/t/t6034-merge-rename-nocruft.sh index 65be95fba..65be95fba 100755 --- a/t/t6023-merge-rename-nocruft.sh +++ b/t/t6034-merge-rename-nocruft.sh diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 69501e271..1c27ffb45 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -185,8 +185,9 @@ cba EOF test_expect_success \ 'listing tags with substring as pattern must print those matching' ' - git tag -l "*a*" > actual && - test_cmp expect actual + rm *a* && + git tag -l "*a*" > current && + test_cmp expect current ' cat >expect <<EOF @@ -582,7 +583,7 @@ test_expect_success \ # subsequent tests require gpg; check if it is available gpg --version >/dev/null if [ $? -eq 127 ]; then - echo "gpg not found - skipping tag signing and verification tests" + say "gpg not found - skipping tag signing and verification tests" test_done exit fi @@ -614,7 +615,7 @@ test_expect_success \ # that version, creation of signed tags using the generated key fails. case "$(gpg --version)" in 'gpg (GnuPG) 1.0.6'*) - echo "Skipping signed tag tests, because a bug in 1.0.6 version" + say "Skipping signed tag tests, because a bug in 1.0.6 version" test_done exit ;; diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh index 2d919d69e..2f8404afb 100755 --- a/t/t7005-editor.sh +++ b/t/t7005-editor.sh @@ -87,30 +87,27 @@ do ' done +if ! echo 'echo space > "$1"' > "e space.sh" +then + say "Skipping; FS does not support spaces in filenames" + test_done + exit +fi + test_expect_success 'editor with a space' ' - if echo "echo space > \"\$1\"" > "e space.sh" - then - chmod a+x "e space.sh" && - GIT_EDITOR="./e\ space.sh" git commit --amend && - test space = "$(git show -s --pretty=format:%s)" - else - say "Skipping; FS does not support spaces in filenames" - fi + chmod a+x "e space.sh" && + GIT_EDITOR="./e\ space.sh" git commit --amend && + test space = "$(git show -s --pretty=format:%s)" ' unset GIT_EDITOR test_expect_success 'core.editor with a space' ' - if test -f "e space.sh" - then - git config core.editor \"./e\ space.sh\" && - git commit --amend && - test space = "$(git show -s --pretty=format:%s)" - else - say "Skipping; FS does not support spaces in filenames" - fi + git config core.editor \"./e\ space.sh\" && + git commit --amend && + test space = "$(git show -s --pretty=format:%s)" ' diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index 1636fac2a..929d5d4d3 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -373,9 +373,9 @@ test_expect_success 'removal failure' ' mkdir foo && touch foo/bar && - exec <foo/bar && - chmod 0 foo && - test_must_fail git clean -f -d + (exec <foo/bar && + chmod 0 foo && + test_must_fail git clean -f -d) ' chmod 755 foo diff --git a/t/t7502-status.sh b/t/t7508-status.sh index 93f875f50..93f875f50 100755 --- a/t/t7502-status.sh +++ b/t/t7508-status.sh diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 08d5b91c9..e426c96fb 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -88,7 +88,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'Verify commandline' \ - 'diff commandline1 expected' + 'test_cmp expected commandline1' cat >expected-show-all-headers <<\EOF 0001-Second.patch @@ -531,4 +531,15 @@ test_expect_success 'feed two files' ' test "z$(sed -n -e 2p subjects)" = "zSubject: [PATCH 2/2] add master" ' +test_expect_success 'in-reply-to but no threading' ' + git send-email \ + --dry-run \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --in-reply-to="<in-reply-id@example.com>" \ + --no-thread \ + $patches | + grep "In-Reply-To: <in-reply-id@example.com>" +' + test_done diff --git a/t/t9108-git-svn-multi-glob.sh b/t/t9109-git-svn-multi-glob.sh index 8f79c3f25..8f79c3f25 100755 --- a/t/t9108-git-svn-multi-glob.sh +++ b/t/t9109-git-svn-multi-glob.sh diff --git a/t/t9106-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh index fd185011b..fd185011b 100755 --- a/t/t9106-git-svn-dcommit-clobber-series.sh +++ b/t/t9137-git-svn-dcommit-clobber-series.sh diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index 245a7c366..d28b71b8c 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -9,7 +9,7 @@ test_description='Test export of commits to CVS' cvs >/dev/null 2>&1 if test $? -ne 1 then - test_expect_success 'skipping git cvsexportcommit tests, cvs not found' : + say 'skipping git cvsexportcommit tests, cvs not found' test_done exit fi diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index 6a37f71d1..466240cd4 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -13,12 +13,12 @@ cvs CLI client via git-cvsserver server' cvs >/dev/null 2>&1 if test $? -ne 1 then - test_expect_success 'skipping git-cvsserver tests, cvs not found' : + say 'skipping git-cvsserver tests, cvs not found' test_done exit fi perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { - test_expect_success 'skipping git-cvsserver tests, Perl SQLite interface unavailable' : + say 'skipping git-cvsserver tests, Perl SQLite interface unavailable' test_done exit } @@ -44,7 +44,7 @@ test_expect_success 'setup' ' git add secondrootfile && git commit -m "second root") && git pull secondroot master && - git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && + git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" ' @@ -267,7 +267,7 @@ test_expect_success 'gitcvs.ext.dbname' \ rm -fr "$SERVERDIR" cd "$WORKDIR" && -git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && +git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" || exit 1 diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh index e27a1c5f8..888223013 100755 --- a/t/t9401-git-cvsserver-crlf.sh +++ b/t/t9401-git-cvsserver-crlf.sh @@ -49,12 +49,12 @@ not_present() { cvs >/dev/null 2>&1 if test $? -ne 1 then - test_expect_success 'skipping git-cvsserver tests, cvs not found' : + say 'skipping git-cvsserver tests, cvs not found' test_done exit fi perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { - test_expect_success 'skipping git-cvsserver tests, Perl SQLite interface unavailable' : + say 'skipping git-cvsserver tests, Perl SQLite interface unavailable' test_done exit } @@ -84,7 +84,7 @@ test_expect_success 'setup' ' echo "subdir/file.h crlf" >> .gitattributes && git add .gitattributes textfile.c binfile.bin mixedUp.c subdir/* && git commit -q -m "First Commit" && - git clone -q --local --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && + git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" ' diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 6ed10d093..dce06bcc9 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -63,18 +63,10 @@ gitweb_run () { # gitweb.log is left for debugging } -safe_chmod () { - chmod "$1" "$2" && - if [ "$(git config --get core.filemode)" = false ] - then - git update-index --chmod="$1" "$2" - fi -} - . ./test-lib.sh perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || { - test_expect_success 'skipping gitweb tests, perl version is too old' : + say 'skipping gitweb tests, perl version is too old' test_done exit } @@ -242,7 +234,7 @@ test_debug 'cat gitweb.log' test_expect_success \ 'commitdiff(0): mode change' \ - 'safe_chmod +x new_file && + 'test_chmod +x new_file && git commit -a -m "Mode changed." && gitweb_run "p=.git;a=commitdiff"' test_debug 'cat gitweb.log' @@ -281,7 +273,7 @@ test_debug 'cat gitweb.log' test_expect_success \ 'commitdiff(0): mode change and modified' \ 'echo "New line" >> file2 && - safe_chmod +x file2 && + test_chmod +x file2 && git commit -a -m "Mode change and modification." && gitweb_run "p=.git;a=commitdiff"' test_debug 'cat gitweb.log' @@ -308,7 +300,7 @@ test_expect_success \ 'commitdiff(0): renamed, mode change and modified' \ 'git mv file3 file2 && echo "Propter nomen suum." >> file2 && - safe_chmod +x file2 && + test_chmod +x file2 && git commit -a -m "File rename, mode change and modification." && gitweb_run "p=.git;a=commitdiff"' test_debug 'cat gitweb.log' @@ -425,10 +417,10 @@ test_expect_success \ git add 03-new && git mv 04-rename-from 04-rename-to && echo "Changed" >> 04-rename-to && - safe_chmod +x 05-mode-change && + test_chmod +x 05-mode-change && rm -f 06-file-or-symlink && ln -s 01-change 06-file-or-symlink && echo "Changed and have mode changed" > 07-change-mode-change && - safe_chmod +x 07-change-mode-change && + test_chmod +x 07-change-mode-change && git commit -a -m "Large commit" && git checkout master' diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh index b81d5dfc3..4a501c684 100755 --- a/t/t9700-perl-git.sh +++ b/t/t9700-perl-git.sh @@ -7,7 +7,7 @@ test_description='perl interface (Git.pm)' . ./test-lib.sh perl -MTest::More -e 0 2>/dev/null || { - say_color skip "Perl Test::More unavailable, skipping test" + say "Perl Test::More unavailable, skipping test" test_done } diff --git a/t/test-lib.sh b/t/test-lib.sh index 7a847ecbd..638cca41e 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -238,18 +238,25 @@ test_merge () { git tag "$1" } +# This function helps systems where core.filemode=false is set. +# Use it instead of plain 'chmod +x' to set or unset the executable bit +# of a file in the working directory and add it to the index. + +test_chmod () { + chmod "$@" && + git update-index --add "--chmod=$@" +} + # You are not expected to call test_ok_ and test_failure_ directly, use # the text_expect_* functions instead. test_ok_ () { - test_count=$(expr "$test_count" + 1) - test_success=$(expr "$test_success" + 1) + test_success=$(($test_success + 1)) say_color "" " ok $test_count: $@" } test_failure_ () { - test_count=$(expr "$test_count" + 1) - test_failure=$(expr "$test_failure" + 1); + test_failure=$(($test_failure + 1)) say_color error "FAIL $test_count: $1" shift echo "$@" | sed -e 's/^/ /' @@ -257,13 +264,11 @@ test_failure_ () { } test_known_broken_ok_ () { - test_count=$(expr "$test_count" + 1) test_fixed=$(($test_fixed+1)) say_color "" " FIXED $test_count: $@" } test_known_broken_failure_ () { - test_count=$(expr "$test_count" + 1) test_broken=$(($test_broken+1)) say_color skip " still broken $test_count: $@" } @@ -279,12 +284,11 @@ test_run_ () { } test_skip () { - this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$') - this_test="$this_test.$(expr "$test_count" + 1)" + test_count=$(($test_count+1)) to_skip= for skp in $GIT_SKIP_TESTS do - case "$this_test" in + case $this_test.$test_count in $skp) to_skip=t esac @@ -292,7 +296,6 @@ test_skip () { case "$to_skip" in t) say_color skip >&3 "skipping test: $@" - test_count=$(expr "$test_count" + 1) say_color skip "skip $test_count: $1" : true ;; @@ -370,7 +373,7 @@ test_external () { then # Announce the script to reduce confusion about the # test output that follows. - say_color "" " run $(expr "$test_count" + 1): $descr ($*)" + say_color "" " run $test_count: $descr ($*)" # Run command; redirect its stderr to &4 as in # test_run_, but keep its stdout on our stdout even in # non-verbose mode. @@ -464,7 +467,7 @@ test_done () { trap - EXIT test_results_dir="$TEST_DIRECTORY/test-results" mkdir -p "$test_results_dir" - test_results_path="$test_results_dir/${0%-*}-$$" + test_results_path="$test_results_dir/${0%.sh}-$$" echo "total $test_count" >> $test_results_path echo "success $test_success" >> $test_results_path @@ -613,7 +616,8 @@ test_create_repo "$test" # in subprocesses like git equals our $PWD (for pathname comparisons). cd -P "$test" || exit 1 -this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$') +this_test=${0##*/} +this_test=${this_test%%-*} for skp in $GIT_SKIP_TESTS do to_skip= diff --git a/transport.c b/transport.c index 9ad4a16c3..9ae92cd39 100644 --- a/transport.c +++ b/transport.c @@ -138,6 +138,11 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list) } } +static const char *rsync_url(const char *url) +{ + return prefixcmp(url, "rsync://") ? skip_prefix(url, "rsync:") : url; +} + static struct ref *get_refs_via_rsync(struct transport *transport) { struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT; @@ -153,7 +158,7 @@ static struct ref *get_refs_via_rsync(struct transport *transport) die ("Could not make temporary directory"); temp_dir_len = temp_dir.len; - strbuf_addstr(&buf, transport->url); + strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addstr(&buf, "/refs"); memset(&rsync, 0, sizeof(rsync)); @@ -169,7 +174,7 @@ static struct ref *get_refs_via_rsync(struct transport *transport) die ("Could not run rsync to get refs"); strbuf_reset(&buf); - strbuf_addstr(&buf, transport->url); + strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addstr(&buf, "/packed-refs"); args[2] = buf.buf; @@ -206,7 +211,7 @@ static int fetch_objs_via_rsync(struct transport *transport, const char *args[8]; int result; - strbuf_addstr(&buf, transport->url); + strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addstr(&buf, "/objects/"); memset(&rsync, 0, sizeof(rsync)); @@ -285,7 +290,7 @@ static int rsync_transport_push(struct transport *transport, /* first push the objects */ - strbuf_addstr(&buf, transport->url); + strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addch(&buf, '/'); memset(&rsync, 0, sizeof(rsync)); @@ -306,7 +311,8 @@ static int rsync_transport_push(struct transport *transport, args[i++] = NULL; if (run_command(&rsync)) - return error("Could not push objects to %s", transport->url); + return error("Could not push objects to %s", + rsync_url(transport->url)); /* copy the refs to the temporary directory; they could be packed. */ @@ -327,10 +333,11 @@ static int rsync_transport_push(struct transport *transport, if (!(flags & TRANSPORT_PUSH_FORCE)) args[i++] = "--ignore-existing"; args[i++] = temp_dir.buf; - args[i++] = transport->url; + args[i++] = rsync_url(transport->url); args[i++] = NULL; if (run_command(&rsync)) - result = error("Could not push to %s", transport->url); + result = error("Could not push to %s", + rsync_url(transport->url)); if (remove_dir_recursively(&temp_dir, 0)) warning ("Could not remove temporary directory %s.", @@ -723,7 +730,7 @@ struct transport *transport_get(struct remote *remote, const char *url) ret->remote = remote; ret->url = url; - if (!prefixcmp(url, "rsync://")) { + if (!prefixcmp(url, "rsync:")) { ret->get_refs_list = get_refs_via_rsync; ret->fetch = fetch_objs_via_rsync; ret->push = rsync_transport_push; diff --git a/unpack-trees.c b/unpack-trees.c index e547282ed..86e28650b 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -49,39 +49,20 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce, memcpy(new, ce, size); new->next = NULL; new->ce_flags = (new->ce_flags & ~clear) | set; - add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK); + add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); } -/* Unlink the last component and attempt to remove leading - * directories, in case this unlink is the removal of the - * last entry in the directory -- empty directories are removed. +/* + * Unlink the last component and schedule the leading directories for + * removal, such that empty directories get removed. */ static void unlink_entry(struct cache_entry *ce) { - char *cp, *prev; - char *name = ce->name; - - if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name)) + if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce))) return; - if (unlink(name)) + if (unlink(ce->name)) return; - prev = NULL; - while (1) { - int status; - cp = strrchr(name, '/'); - if (prev) - *prev = '/'; - if (!cp) - break; - - *cp = 0; - status = rmdir(name); - if (status) { - *cp = '/'; - break; - } - prev = cp; - } + schedule_dir_for_removal(ce->name, ce_namelen(ce)); } static struct checkout state; @@ -112,11 +93,10 @@ static int check_updates(struct unpack_trees_options *o) display_progress(progress, ++cnt); if (o->update) unlink_entry(ce); - remove_index_entry_at(&o->result, i); - i--; - continue; } } + remove_marked_cache_entries(&o->result); + remove_scheduled_dirs(); for (i = 0; i < index->cache_nr; i++) { struct cache_entry *ce = index->cache[i]; @@ -286,9 +266,9 @@ static int unpack_nondirectories(int n, unsigned long mask, if (o->merge) return call_unpack_fn(src, o); - n += o->merge; for (i = 0; i < n; i++) - add_entry(o, src[i], 0, 0); + if (src[i] && src[i] != o->df_conflict_entry) + add_entry(o, src[i], 0, 0); return 0; } @@ -380,8 +360,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options memset(&o->result, 0, sizeof(o->result)); o->result.initialized = 1; - if (o->src_index) - o->result.timestamp = o->src_index->timestamp; + if (o->src_index) { + o->result.timestamp.sec = o->src_index->timestamp.sec; + o->result.timestamp.nsec = o->src_index->timestamp.nsec; + } o->merge_size = len; if (!dfc) @@ -446,7 +428,7 @@ static int verify_uptodate(struct cache_entry *ce, { struct stat st; - if (o->index_only || o->reset) + if (o->index_only || o->reset || ce_uptodate(ce)) return 0; if (!lstat(ce->name, &st)) { @@ -583,7 +565,7 @@ static int verify_absent(struct cache_entry *ce, const char *action, if (o->index_only || o->reset || !o->update) return 0; - if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name)) + if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce))) return 0; if (!lstat(ce->name, &st)) { diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c index 3e97462bd..02184d9cd 100644 --- a/xdiff/xdiffi.c +++ b/xdiff/xdiffi.c @@ -293,15 +293,14 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1, for (; off1 < lim1; off1++) rchg1[rindex1[off1]] = 1; } else { - long ec; xdpsplit_t spl; spl.i1 = spl.i2 = 0; /* * Divide ... */ - if ((ec = xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb, - need_min, &spl, xenv)) < 0) { + if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb, + need_min, &spl, xenv) < 0) { return -1; } diff --git a/xdiff/xemit.c b/xdiff/xemit.c index 05bfa41f1..c4bedf0d1 100644 --- a/xdiff/xemit.c +++ b/xdiff/xemit.c @@ -132,7 +132,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, if (xecfg->flags & XDL_EMIT_COMMON) return xdl_emit_common(xe, xscr, ecb, xecfg); - for (xch = xche = xscr; xch; xch = xche->next) { + for (xch = xscr; xch; xch = xche->next) { xche = xdl_get_hunk(xch, xecfg); s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0); |