diff options
138 files changed, 2671 insertions, 1069 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile index 43781fb248..4144d1e086 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -2,8 +2,8 @@ MAN1_TXT= \ $(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \ $(wildcard git-*.txt)) \ gitk.txt -MAN5_TXT=gitattributes.txt gitignore.txt gitcli.txt gitmodules.txt -MAN7_TXT=git.txt +MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt githooks.txt +MAN7_TXT=git.txt gitcli.txt MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT) MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT)) @@ -18,7 +18,6 @@ ARTICLES += cvs-migration ARTICLES += diffcore ARTICLES += howto-index ARTICLES += repository-layout -ARTICLES += hooks ARTICLES += everyday ARTICLES += git-tools ARTICLES += glossary diff --git a/Documentation/config.txt b/Documentation/config.txt index 00f089fee4..217980f48d 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -399,6 +399,21 @@ branch.autosetupmerge:: done when the starting point is either a local branch or remote branch. This option defaults to true. +branch.autosetuprebase:: + When a new branch is created with `git-branch` or `git-checkout` + that tracks another branch, this variable tells git to set + up pull to rebase instead of merge (see "branch.<name>.rebase"). + When `never`, rebase is never automatically set to true. + When `local`, rebase is set to true for tracked branches of + other local branches. + When `remote`, rebase is set to true for tracked branches of + remote branches. + When `always`, rebase will be set to true for all tracking + branches. + See "branch.autosetupmerge" for details on how to set up a + branch to track another branch. + This option defaults to never. + branch.<name>.remote:: When in branch <name>, it tells `git fetch` which remote to fetch. If this option is not given, `git fetch` defaults to remote "origin". @@ -426,7 +441,8 @@ branch.<name>.mergeoptions:: branch.<name>.rebase:: When true, rebase the branch <name> on top of the fetched branch, - instead of merging the default branch from the default remote. + instead of merging the default branch from the default remote when + "git pull" is run. *NOTE*: this is a possibly dangerous operation; do *not* use it unless you understand the implications (see linkgit:git-rebase[1] for details). @@ -684,6 +700,36 @@ specified as 'gitcvs.<access_method>.<varname>' (where 'access_method' is one of "ext" and "pserver") to make them apply only for the given access method. +gui.commitmsgwidth:: + Defines how wide the commit message window is in the + linkgit:git-gui[1]. "75" is the default. + +gui.diffcontext:: + Specifies how many context lines should be used in calls to diff + made by the linkgit:git-gui[1]. The default is "5". + +gui.matchtrackingbranch:: + Determines if new branches created with linkgit:git-gui[1] should + default to tracking remote branches with matching names or + not. Default: "false". + +gui.newbranchtemplate:: + Is used as suggested name when creating new branches using the + linkgit:git-gui[1]. + +gui.pruneduringfetch:: + "true" if linkgit:git-gui[1] should prune tracking branches when + performing a fetch. The default value is "false". + +gui.trustmtime:: + Determines if linkgit:git-gui[1] should trust the file modification + timestamp or not. By default the timestamps are not trusted. + +gui.spellingdictionary:: + Specifies the dictionary used for spell checking commit messages in + the linkgit:git-gui[1]. When set to "none" spell checking is turned + off. + help.browser:: Specify the browser that will be used to display help in the 'web' format. See linkgit:git-help[1]. @@ -779,37 +825,16 @@ man.viewer:: Specify the programs that may be used to display help in the 'man' format. See linkgit:git-help[1]. -merge.summary:: - Whether to include summaries of merged commits in newly created - merge commit messages. False by default. - -merge.tool:: - Controls which merge resolution program is used by - linkgit:git-mergetool[1]. Valid built-in values are: "kdiff3", - "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and - "opendiff". Any other value is treated is custom merge tool - and there must be a corresponing mergetool.<tool>.cmd option. - -merge.verbosity:: - Controls the amount of output shown by the recursive merge - strategy. Level 0 outputs nothing except a final error - message if conflicts were detected. Level 1 outputs only - conflicts, 2 outputs conflicts and file changes. Level 5 and - above outputs debugging information. The default is level 2. - Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable. - -merge.<driver>.name:: - Defines a human readable name for a custom low-level - merge driver. See linkgit:gitattributes[5] for details. - -merge.<driver>.driver:: - Defines the command that implements a custom low-level - merge driver. See linkgit:gitattributes[5] for details. - -merge.<driver>.recursive:: - Names a low-level merge driver to be used when - performing an internal merge between common ancestors. - See linkgit:gitattributes[5] for details. +include::merge-config.txt[] + +man.<tool>.cmd:: + Specify the command to invoke the specified man viewer. The + specified command is evaluated in shell with the man page + passed as argument. (See linkgit:git-help[1].) + +man.<tool>.path:: + Override the path for the given tool that may be used to + display help in the 'man' format. See linkgit:git-help[1]. mergetool.<tool>.path:: Override the path for the given tool. This is useful in case diff --git a/Documentation/cvs-migration.txt b/Documentation/cvs-migration.txt index 00f2e36b2e..374bc87b10 100644 --- a/Documentation/cvs-migration.txt +++ b/Documentation/cvs-migration.txt @@ -137,7 +137,7 @@ Advanced Shared Repository Management Git allows you to specify scripts called "hooks" to be run at certain points. You can use these, for example, to send all commits to the shared -repository to a mailing list. See link:hooks.html[Hooks used by git]. +repository to a mailing list. See linkgit:githooks[5][Hooks used by git]. You can enforce finer grained permissions using update hooks. See link:howto/update-hook-example.txt[Controlling access to branches using diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 35e67a06e4..e0e730b6c4 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -71,7 +71,9 @@ OPTIONS the specified filepatterns before exiting. -u:: - Update only files that git already knows about. This is similar + Update only files that git already knows about, staging modified + content for commit and marking deleted files for removal. This + is similar to what "git commit -a" does in preparation for making a commit, except that the update is limited to paths specified on the command line. If no paths are specified, all tracked files in the @@ -98,21 +100,27 @@ those in info/exclude. See link:repository-layout.html[repository layout]. EXAMPLES -------- -git-add Documentation/\\*.txt:: - Adds content from all `\*.txt` files under `Documentation` - directory and its subdirectories. +* Adds content from all `\*.txt` files under `Documentation` directory +and its subdirectories: ++ +------------ +$ git add Documentation/\\*.txt +------------ + Note that the asterisk `\*` is quoted from the shell in this example; this lets the command to include the files from subdirectories of `Documentation/` directory. -git-add git-*.sh:: - - Considers adding content from all git-*.sh scripts. - Because this example lets shell expand the asterisk - (i.e. you are listing the files explicitly), it does not - consider `subdir/git-foo.sh`. +* Considers adding content from all git-*.sh scripts: ++ +------------ +$ git add git-*.sh +------------ ++ +Because this example lets shell expand the asterisk (i.e. you are +listing the files explicitly), it does not consider +`subdir/git-foo.sh`. Interactive mode ---------------- diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 698ffde7ce..0b8b0ebba7 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -85,7 +85,7 @@ Oh, and then after you want to reset to the original head, do a $ git bisect reset ------------------------------------------------ -to get back to the master branch, instead of being in one of the +to get back to the original branch, instead of being in one of the bisection branches ("git bisect start" will do that for you too, actually: it will reset the bisection state, and before it does that it checks that you're not using some old bisection branch). @@ -224,6 +224,55 @@ tree to the pristine state. Finally the "run" script can exit with the status of the real test to let "git bisect run" command loop to know the outcome. +EXAMPLES +-------- + +* Automatically bisect a broken build between v1.2 and HEAD: ++ +------------ +$ git bisect start HEAD v1.2 -- # HEAD is bad, v1.2 is good +$ git bisect run make # "make" builds the app +------------ + +* Automatically bisect a broken test suite: ++ +------------ +$ cat ~/test.sh +#!/bin/sh +make || exit 125 # this "skip"s broken builds +make test # "make test" runs the test suite +$ git bisect start v1.3 v1.1 -- # v1.3 is bad, v1.1 is good +$ git bisect run ~/test.sh +------------ ++ +Here we use a "test.sh" custom script. In this script, if "make" +fails, we "skip" the current commit. ++ +It's safer to use a custom script outside the repo to prevent +interactions between the bisect, make and test processes and the +script. ++ +And "make test" should "exit 0", if the test suite passes, and +"exit 1" (for example) otherwise. + +* Automatically bisect a broken test case: ++ +------------ +$ cat ~/test.sh +#!/bin/sh +make || exit 125 # this "skip"s broken builds +~/check_test_case.sh # does the test case passes ? +$ git bisect start HEAD HEAD~10 -- # culprit is among the last 10 +$ git bisect run ~/test.sh +------------ ++ +Here "check_test_case.sh" should "exit 0", if the test case passes, +and "exit 1" (for example) otherwise. ++ +It's safer if both "test.sh" and "check_test_case.sh" scripts are +outside the repo to prevent interactions between the bisect, make and +test processes and the scripts. + Author ------ Written by Linus Torvalds <torvalds@osdl.org> diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index f0beb412e6..ca048f46f6 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit SYNOPSIS -------- -'git-cherry-pick' [--edit] [-n] [-m parent-number] [-x] <commit> +'git-cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] <commit> DESCRIPTION ----------- @@ -64,6 +64,9 @@ OPTIONS This is useful when cherry-picking more than one commits' effect to your working tree in a row. +-s|--signoff:: + Add Signed-off-by line at the end of the commit message. + Author ------ diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 4bb51cc06e..c3c9f5b902 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -87,7 +87,7 @@ OPTIONS --no-verify:: This option bypasses the pre-commit and commit-msg hooks. - See also link:hooks.html[hooks]. + See also linkgit:githooks[5][hooks]. --allow-empty:: Usually recording a commit that has the exact same tree as its @@ -292,7 +292,7 @@ order). HOOKS ----- This command can run `commit-msg`, `prepare-commit-msg`, `pre-commit`, -and `post-commit` hooks. See link:hooks.html[hooks] for more +and `post-commit` hooks. See linkgit:githooks[5][hooks] for more information. diff --git a/Documentation/git-fmt-merge-msg.txt b/Documentation/git-fmt-merge-msg.txt index 8615ae353e..457cf42561 100644 --- a/Documentation/git-fmt-merge-msg.txt +++ b/Documentation/git-fmt-merge-msg.txt @@ -9,8 +9,8 @@ git-fmt-merge-msg - Produce a merge commit message SYNOPSIS -------- [verse] -git-fmt-merge-msg [--summary | --no-summary] <$GIT_DIR/FETCH_HEAD -git-fmt-merge-msg [--summary | --no-summary] -F <file> +git-fmt-merge-msg [--log | --no-log] <$GIT_DIR/FETCH_HEAD +git-fmt-merge-msg [--log | --no-log] -F <file> DESCRIPTION ----------- @@ -24,15 +24,19 @@ automatically invoking `git-merge`. OPTIONS ------- ---summary:: +--log:: In addition to branch names, populate the log message with one-line descriptions from the actual commits that are being merged. ---no-summary:: +--no-log:: Do not list one-line descriptions from the actual commits being merged. +--summary,--no-summary:: + Synonyms to --log and --no-log; these are deprecated and will be + removed in the future. + --file <file>, -F <file>:: Take the list of merged objects from <file> instead of stdin. @@ -40,10 +44,14 @@ OPTIONS CONFIGURATION ------------- -merge.summary:: +merge.log:: Whether to include summaries of merged commits in newly merge commit messages. False by default. +merge.summary:: + Synonym to `merge.log`; this is deprecated and will be removed in + the future. + SEE ALSO -------- linkgit:git-merge[1] diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index b5207b7604..c60ce123ec 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -156,6 +156,12 @@ want a filename like `0001-description-of-my-change.patch`, and the first letter does not have to be a dot. Leaving it empty would not add any suffix. +--no-binary:: + Don't output contents of changes in binary files, just take note + that they differ. Note that this disable the patch to be properly + applied. By default the contents of changes in those files are + encoded in the patch. + CONFIGURATION ------------- You can specify extra mail header lines to be added to each message @@ -168,38 +174,54 @@ and file suffix, and number patches when outputting more than one. subjectprefix = CHANGE suffix = .txt numbered = auto + cc = <email> ------------ EXAMPLES -------- -git-format-patch -k --stdout R1..R2 | git-am -3 -k:: - Extract commits between revisions R1 and R2, and apply - them on top of the current branch using `git-am` to - cherry-pick them. - -git-format-patch origin:: - Extract all commits which are in the current branch but - not in the origin branch. For each commit a separate file - is created in the current directory. - -git-format-patch \--root origin:: - Extract all commits that lead to 'origin' since the - inception of the project. - -git-format-patch -M -B origin:: - The same as the previous one. Additionally, it detects - and handles renames and complete rewrites intelligently to - produce a renaming patch. A renaming patch reduces the - amount of text output, and generally makes it easier to - review it. Note that the "patch" program does not - understand renaming patches, so use it only when you know - the recipient uses git to apply your patch. - -git-format-patch -3:: - Extract three topmost commits from the current branch - and format them as e-mailable patches. +* Extract commits between revisions R1 and R2, and apply them on top of +the current branch using `git-am` to cherry-pick them: ++ +------------ +$ git format-patch -k --stdout R1..R2 | git-am -3 -k +------------ + +* Extract all commits which are in the current branch but not in the +origin branch: ++ +------------ +$ git format-patch origin +------------ ++ +For each commit a separate file is created in the current directory. + +* Extract all commits that lead to 'origin' since the inception of the +project: ++ +------------ +$ git format-patch \--root origin +------------ + +* The same as the previous one: ++ +------------ +$ git format-patch -M -B origin +------------ ++ +Additionally, it detects and handles renames and complete rewrites +intelligently to produce a renaming patch. A renaming patch reduces +the amount of text output, and generally makes it easier to review it. +Note that the "patch" program does not understand renaming patches, so +use it only when you know the recipient uses git to apply your patch. + +* Extract three topmost commits from the current branch and format them +as e-mailable patches: ++ +------------ +$ git format-patch -3 +------------ See Also -------- diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt index be2ae53b90..bfbba9e235 100644 --- a/Documentation/git-help.txt +++ b/Documentation/git-help.txt @@ -82,28 +82,75 @@ man.viewer ~~~~~~~~~~ The 'man.viewer' config variable will be checked if the 'man' format -is chosen. Only the following values are currently supported: +is chosen. The following values are currently supported: * "man": use the 'man' program as usual, * "woman": use 'emacsclient' to launch the "woman" mode in emacs (this only works starting with emacsclient versions 22), -* "konqueror": use a man KIO slave in konqueror. +* "konqueror": use 'kfmclient' to open the man page in a new konqueror +tab (see 'Note about konqueror' below). -Multiple values may be given to this configuration variable. Their -corresponding programs will be tried in the order listed in the -configuration file. +Values for other tools can be used if there is a corresponding +'man.<tool>.cmd' configuration entry (see below). + +Multiple values may be given to the 'man.viewer' configuration +variable. Their corresponding programs will be tried in the order +listed in the configuration file. For example, this configuration: +------------------------------------------------ [man] viewer = konqueror viewer = woman +------------------------------------------------ will try to use konqueror first. But this may fail (for example if DISPLAY is not set) and in that case emacs' woman mode will be tried. If everything fails the 'man' program will be tried anyway. +man.<tool>.path +~~~~~~~~~~~~~~~ + +You can explicitly provide a full path to your preferred man viewer by +setting the configuration variable 'man.<tool>.path'. For example, you +can configure the absolute path to konqueror by setting +'man.konqueror.path'. Otherwise, 'git help' assumes the tool is +available in PATH. + +man.<tool>.cmd +~~~~~~~~~~~~~~ + +When the man viewer, specified by the 'man.viewer' configuration +variables, is not among the supported ones, then the corresponding +'man.<tool>.cmd' configuration variable will be looked up. If this +variable exists then the specified tool will be treated as a custom +command and a shell eval will be used to run the command with the man +page passed as arguments. + +Note about konqueror +~~~~~~~~~~~~~~~~~~~~ + +When 'konqueror' is specified in the 'man.viewer' configuration +variable, we launch 'kfmclient' to try to open the man page on an +already opened konqueror in a new tab if possible. + +For consistency, we also try such a trick if 'man.konqueror.path' is +set to something like 'A_PATH_TO/konqueror'. That means we will try to +launch 'A_PATH_TO/kfmclient' instead. + +If you really want to use 'konqueror', then you can use something like +the following: + +------------------------------------------------ + [man] + viewer = konq + + [man "konq"] + cmd = A_PATH_TO/konqueror +------------------------------------------------ + Note about git config --global ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index c136b10692..ef1f055c85 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -9,7 +9,7 @@ git-merge - Join two or more development histories together SYNOPSIS -------- [verse] -'git-merge' [-n] [--summary] [--no-commit] [--squash] [-s <strategy>]... +'git-merge' [-n] [--stat] [--no-commit] [--squash] [-s <strategy>]... [-m <msg>] <remote> <remote>... 'git-merge' <msg> HEAD <remote>... @@ -46,18 +46,7 @@ linkgit:git-reset[1]. CONFIGURATION ------------- - -merge.summary:: - Whether to include summaries of merged commits in newly - created merge commit. False by default. - -merge.verbosity:: - Controls the amount of output shown by the recursive merge - strategy. Level 0 outputs nothing except a final error - message if conflicts were detected. Level 1 outputs only - conflicts, 2 outputs conflicts and file changes. Level 5 and - above outputs debugging information. The default is level 2. - Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable. +include::merge-config.txt[] branch.<name>.mergeoptions:: Sets default options for merging into branch <name>. The syntax and diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index 3405ca09e8..66304f0255 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -111,40 +111,58 @@ rules apply: EXAMPLES -------- -git pull, git pull origin:: - Update the remote-tracking branches for the repository - you cloned from, then merge one of them into your - current branch. Normally the branch merged in is - the HEAD of the remote repository, but the choice is - determined by the branch.<name>.remote and - branch.<name>.merge options; see linkgit:git-config[1] - for details. - -git pull origin next:: - Merge into the current branch the remote branch `next`; - leaves a copy of `next` temporarily in FETCH_HEAD, but - does not update any remote-tracking branches. - -git pull . fixes enhancements:: - Bundle local branch `fixes` and `enhancements` on top of - the current branch, making an Octopus merge. This `git pull .` - syntax is equivalent to `git merge`. - -git pull -s ours . obsolete:: - Merge local branch `obsolete` into the current branch, - using `ours` merge strategy. - -git pull --no-commit . maint:: - Merge local branch `maint` into the current branch, but - do not make a commit automatically. This can be used - when you want to include further changes to the merge, - or want to write your own merge commit message. +* Update the remote-tracking branches for the repository + you cloned from, then merge one of them into your + current branch: ++ +------------------------------------------------ +$ git pull, git pull origin +------------------------------------------------ ++ +Normally the branch merged in is the HEAD of the remote repository, +but the choice is determined by the branch.<name>.remote and +branch.<name>.merge options; see linkgit:git-config[1] for details. + +* Merge into the current branch the remote branch `next`: ++ +------------------------------------------------ +$ git pull origin next +------------------------------------------------ ++ +This leaves a copy of `next` temporarily in FETCH_HEAD, but +does not update any remote-tracking branches. + +* Bundle local branch `fixes` and `enhancements` on top of + the current branch, making an Octopus merge: ++ +------------------------------------------------ +$ git pull . fixes enhancements +------------------------------------------------ ++ +This `git pull .` syntax is equivalent to `git merge`. + +* Merge local branch `obsolete` into the current branch, using `ours` + merge strategy: ++ +------------------------------------------------ +$ git pull -s ours . obsolete +------------------------------------------------ + +* Merge local branch `maint` into the current branch, but do not make + a commit automatically: ++ +------------------------------------------------ +$ git pull --no-commit . maint +------------------------------------------------ ++ +This can be used when you want to include further changes to the +merge, or want to write your own merge commit message. + You should refrain from abusing this option to sneak substantial changes into a merge commit. Small fixups like bumping release/version name would be acceptable. -Command line pull of multiple branches from one repository:: +* Command line pull of multiple branches from one repository: + ------------------------------------------------ $ git checkout master @@ -152,12 +170,12 @@ $ git fetch origin +pu:pu maint:tmp $ git pull . tmp ------------------------------------------------ + -This updates (or creates, as necessary) branches `pu` and `tmp` -in the local repository by fetching from the branches -(respectively) `pu` and `maint` from the remote repository. +This updates (or creates, as necessary) branches `pu` and `tmp` in +the local repository by fetching from the branches (respectively) +`pu` and `maint` from the remote repository. + -The `pu` branch will be updated even if it is does not -fast-forward; the others will not be. +The `pu` branch will be updated even if it is does not fast-forward; +the others will not be. + The final command then merges the newly fetched `tmp` into master. diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 110e7ba71f..b6b2fe92a1 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -347,7 +347,7 @@ Each line of options has this format: * Use `*` to mean that this option should not be listed in the usage generated for the `-h` argument. It's shown for `--help-all` as - documented in linkgit:gitcli[5]. + documented in linkgit:gitcli[7]. * Use `!` to not make the corresponding negated long option available. diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt index 93e20f7752..13ceabbcc8 100644 --- a/Documentation/git-revert.txt +++ b/Documentation/git-revert.txt @@ -7,7 +7,7 @@ git-revert - Revert an existing commit SYNOPSIS -------- -'git-revert' [--edit | --no-edit] [-n] [-m parent-number] <commit> +'git-revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit> DESCRIPTION ----------- @@ -51,6 +51,9 @@ OPTIONS This is useful when reverting more than one commits' effect to your working tree in a row. +-s|--signoff:: + Add Signed-off-by line at the end of the commit message. + Author ------ diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index f4ba1056f0..c6b56b4ef3 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -166,11 +166,18 @@ environment). This command has the same behaviour. Any other arguments are passed directly to `git log' 'blame':: - Show what revision and author last modified each line of a file. This is - identical to `git blame', but SVN revision numbers are shown instead of git - commit hashes. + Show what revision and author last modified each line of a file. The + output of this mode is format-compatible with the output of + `svn blame' by default. Like the SVN blame command, + local uncommitted changes in the working copy are ignored; + the version of the file in the HEAD revision is annotated. Unknown + arguments are passed directly to git-blame. + -All arguments are passed directly to `git blame'. +--git-format;; + Produce output in the same format as `git blame', but with + SVN revision numbers instead of git commit hashes. In this mode, + changes that haven't been committed to SVN (including local + working-copy edits) are shown as revision 0. -- 'find-rev':: diff --git a/Documentation/git-web--browse.txt b/Documentation/git-web--browse.txt index ddbae5b194..92ef574565 100644 --- a/Documentation/git-web--browse.txt +++ b/Documentation/git-web--browse.txt @@ -20,7 +20,7 @@ The following browsers (or commands) are currently supported: * firefox (this is the default under X Window when not using KDE) * iceweasel -* konqueror (this is the default under KDE) +* konqueror (this is the default under KDE, see 'Note about konqueror' below) * w3m (this is the default outside graphical environments) * links * lynx @@ -71,6 +71,28 @@ variable exists then "git web--browse" will treat the specified tool as a custom command and will use a shell eval to run the command with the URLs passed as arguments. +Note about konqueror +-------------------- + +When 'konqueror' is specified by the a command line option or a +configuration variable, we launch 'kfmclient' to try to open the HTML +man page on an already opened konqueror in a new tab if possible. + +For consistency, we also try such a trick if 'brower.konqueror.path' is +set to something like 'A_PATH_TO/konqueror'. That means we will try to +launch 'A_PATH_TO/kfmclient' instead. + +If you really want to use 'konqueror', then you can use something like +the following: + +------------------------------------------------ + [web] + browser = konq + + [browser "konq"] + cmd = A_PATH_TO/konqueror +------------------------------------------------ + Note about git config --global ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/git.txt b/Documentation/git.txt index 6f445b1e3b..adcd3e00b2 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -364,7 +364,7 @@ File/Directory Structure Please see the link:repository-layout.html[repository layout] document. -Read link:hooks.html[hooks] for more details about each hook. +Read linkgit:githooks[5][hooks] for more details about each hook. Higher level SCMs may provide and manage additional information in the `$GIT_DIR`. diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt index 7ee5ce386f..835cb05f96 100644 --- a/Documentation/gitcli.txt +++ b/Documentation/gitcli.txt @@ -1,4 +1,4 @@ -gitcli(5) +gitcli(7) ========= NAME diff --git a/Documentation/hooks.txt b/Documentation/githooks.txt index d89cc22261..53747febd2 100644 --- a/Documentation/hooks.txt +++ b/Documentation/githooks.txt @@ -1,5 +1,17 @@ -Hooks used by git -================= +githooks(5) +=========== + +NAME +---- +githooks - Hooks used by git + +SYNOPSIS +-------- +$GIT_DIR/hooks/* + + +DESCRIPTION +----------- Hooks are little scripts you can place in `$GIT_DIR/hooks` directory to trigger action at certain points. When @@ -285,3 +297,7 @@ pre-auto-gc This hook is invoked by `git-gc --auto`. It takes no parameter, and exiting with non-zero status from this script causes the `git-gc --auto` to abort. + +GIT +--- +Part of the linkgit:git[7] suite diff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt new file mode 100644 index 0000000000..48ce747cf4 --- /dev/null +++ b/Documentation/merge-config.txt @@ -0,0 +1,40 @@ +merge.stat:: + Whether to print the diffstat berween ORIG_HEAD and merge result + at the end of the merge. True by default. + +merge.log:: + Whether to include summaries of merged commits in newly created + merge commit messages. False by default. + +merge.renameLimit:: + The number of files to consider when performing rename detection + during a merge; if not specified, defaults to the value of + diff.renameLimit. + +merge.tool:: + Controls which merge resolution program is used by + linkgit:git-mergetool[1]. Valid built-in values are: "kdiff3", + "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and + "opendiff". Any other value is treated is custom merge tool + and there must be a corresponing mergetool.<tool>.cmd option. + +merge.verbosity:: + Controls the amount of output shown by the recursive merge + strategy. Level 0 outputs nothing except a final error + message if conflicts were detected. Level 1 outputs only + conflicts, 2 outputs conflicts and file changes. Level 5 and + above outputs debugging information. The default is level 2. + Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable. + +merge.<driver>.name:: + Defines a human readable name for a custom low-level + merge driver. See linkgit:gitattributes[5] for details. + +merge.<driver>.driver:: + Defines the command that implements a custom low-level + merge driver. See linkgit:gitattributes[5] for details. + +merge.<driver>.recursive:: + Names a low-level merge driver to be used when + performing an internal merge between common ancestors. + See linkgit:gitattributes[5] for details. diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index 9f1fc82550..f37a776489 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -1,10 +1,23 @@ ---summary:: +--stat:: Show a diffstat at the end of the merge. The diffstat is also - controlled by the configuration option merge.diffstat. + controlled by the configuration option merge.stat. --n, \--no-summary:: +-n, \--no-stat:: Do not show diffstat at the end of the merge. +--summary, \--no-summary:: + Synonyms to --stat and --no-stat; these are deprecated and will be + removed in the future. + +--log:: + In addition to branch names, populate the log message with + one-line descriptions from the actual commits that are being + merged. + +--no-log:: + Do not list one-line descriptions from the actual commits being + merged. + --no-commit:: Perform the merge but pretend the merge failed and do not autocommit, to give the user a chance to inspect and diff --git a/Documentation/repository-layout.txt b/Documentation/repository-layout.txt index bbaed2e129..7fd187be8a 100644 --- a/Documentation/repository-layout.txt +++ b/Documentation/repository-layout.txt @@ -124,7 +124,7 @@ hooks:: commands. A handful of sample hooks are installed when `git init` is run, but all of them are disabled by default. To enable, they need to be made executable. - Read link:hooks.html[hooks] for more details about + Read linkgit:githooks[5][hooks] for more details about each hook. index:: diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 86b91a53e5..e2db850150 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -1881,7 +1881,7 @@ $ chmod a+x hooks/post-update (For an explanation of the last two lines, see linkgit:git-update-server-info[1], and the documentation -link:hooks.html[Hooks used by git].) +linkgit:githooks[5][Hooks used by git].) Advertise the URL of proj.git. Anybody else should then be able to clone or pull from that URL, for example with a command line like: @@ -423,6 +423,7 @@ LIB_OBJS += log-tree.o LIB_OBJS += mailmap.o LIB_OBJS += match-trees.o LIB_OBJS += merge-file.o +LIB_OBJS += name-hash.o LIB_OBJS += object.o LIB_OBJS += pack-check.o LIB_OBJS += pack-revindex.o @@ -32,6 +32,21 @@ static int find_tracked_branch(struct remote *remote, void *priv) return 0; } +static int should_setup_rebase(const struct tracking *tracking) +{ + switch (autorebase) { + case AUTOREBASE_NEVER: + return 0; + case AUTOREBASE_LOCAL: + return tracking->remote == NULL; + case AUTOREBASE_REMOTE: + return tracking->remote != NULL; + case AUTOREBASE_ALWAYS: + return 1; + } + return 0; +} + /* * 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 @@ -69,9 +84,14 @@ static int setup_tracking(const char *new_ref, const char *orig_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); - free(tracking.src); 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); return 0; } diff --git a/builtin-apply.c b/builtin-apply.c index caa3f2aa0c..1103625a4a 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -2247,7 +2247,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(new_name, NULL)) + if (has_symlink_leading_path(strlen(new_name), new_name)) return 0; return error("%s: already exists in working directory", new_name); diff --git a/builtin-commit.c b/builtin-commit.c index a113eb0272..0baec6db6a 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -102,7 +102,7 @@ static struct option builtin_commit_options[] = { OPT_BOOLEAN('o', "only", &only, "commit only specified files"), OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"), OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"), - OPT_BOOLEAN(0, "untracked-files", &untracked_files, "show all untracked files"), + OPT_BOOLEAN('u', "untracked-files", &untracked_files, "show all untracked files"), OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"), OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"), @@ -176,9 +176,11 @@ static void add_remove_files(struct path_list *list) { int i; for (i = 0; i < list->nr; i++) { + struct stat st; struct path_list_item *p = &(list->items[i]); - if (file_exists(p->path)) - add_file_to_cache(p->path, 0); + + if (!lstat(p->path, &st)) + add_to_cache(p->path, &st, 0); else remove_file_from_cache(p->path); } diff --git a/builtin-fetch.c b/builtin-fetch.c index e56617e32e..f6584ecea1 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -508,10 +508,8 @@ static void find_non_local_tags(struct transport *transport, will_fetch(head, ref->old_sha1))) { path_list_insert(ref_name, &new_refs); - rm = alloc_ref(strlen(ref_name) + 1); - strcpy(rm->name, ref_name); - rm->peer_ref = alloc_ref(strlen(ref_name) + 1); - strcpy(rm->peer_ref->name, ref_name); + rm = alloc_ref_from_str(ref_name); + rm->peer_ref = alloc_ref_from_str(ref_name); hashcpy(rm->old_sha1, ref_sha1); **tail = rm; diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index 7077d52477..b72cb59e6a 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -6,13 +6,18 @@ #include "tag.h" static const char *fmt_merge_msg_usage = - "git-fmt-merge-msg [--summary] [--no-summary] [--file <file>]"; + "git-fmt-merge-msg [--log] [--no-log] [--file <file>]"; static int merge_summary; static int fmt_merge_msg_config(const char *key, const char *value) { - if (!strcmp("merge.summary", key)) + static int found_merge_log = 0; + if (!strcmp("merge.log", key)) { + found_merge_log = 1; + merge_summary = git_config_bool(key, value); + } + if (!found_merge_log && !strcmp("merge.summary", key)) merge_summary = git_config_bool(key, value); return 0; } @@ -258,9 +263,10 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) git_config(fmt_merge_msg_config); while (argc > 1) { - if (!strcmp(argv[1], "--summary")) + if (!strcmp(argv[1], "--log") || !strcmp(argv[1], "--summary")) merge_summary = 1; - else if (!strcmp(argv[1], "--no-summary")) + else if (!strcmp(argv[1], "--no-log") + || !strcmp(argv[1], "--no-summary")) merge_summary = 0; else if (!strcmp(argv[1], "-F") || !strcmp(argv[1], "--file")) { if (argc < 3) diff --git a/builtin-log.c b/builtin-log.c index 256bbac93a..9d046b2e03 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -485,6 +485,13 @@ static int git_format_config(const char *var, const char *value) fmt_patch_suffix = xstrdup(value); return 0; } + if (!strcmp(var, "format.cc")) { + if (!value) + return config_error_nonbool(var); + ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc); + extra_cc[extra_cc_nr++] = xstrdup(value); + return 0; + } if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { return 0; } @@ -757,6 +764,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) int thread = 0; int cover_letter = 0; int boundary_count = 0; + int no_binary_diff = 0; struct commit *origin = NULL, *head = NULL; const char *in_reply_to = NULL; struct patch_ids ids; @@ -862,6 +870,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) fmt_patch_suffix = argv[i] + 9; else if (!strcmp(argv[i], "--cover-letter")) cover_letter = 1; + else if (!strcmp(argv[i], "--no-binary")) + no_binary_diff = 1; else argv[j++] = argv[i]; } @@ -914,7 +924,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH; - if (!DIFF_OPT_TST(&rev.diffopt, TEXT)) + if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff) DIFF_OPT_SET(&rev.diffopt, BINARY); if (!output_directory && !use_stdout) diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c index 910c0d20e7..46e636fdcf 100644 --- a/builtin-merge-recursive.c +++ b/builtin-merge-recursive.c @@ -92,7 +92,8 @@ static struct path_list current_directory_set = {NULL, 0, 0, 1}; static int call_depth = 0; static int verbosity = 2; -static int rename_limit = -1; +static int diff_rename_limit = -1; +static int merge_rename_limit = -1; static int buffer_output = 1; static struct strbuf obuf = STRBUF_INIT; @@ -361,7 +362,10 @@ static struct path_list *get_renames(struct tree *tree, diff_setup(&opts); DIFF_OPT_SET(&opts, RECURSIVE); opts.detect_rename = DIFF_DETECT_RENAME; - opts.rename_limit = rename_limit; + opts.rename_limit = merge_rename_limit >= 0 ? merge_rename_limit : + diff_rename_limit >= 0 ? diff_rename_limit : + 500; + opts.warn_on_too_large_rename = 1; opts.output_format = DIFF_FORMAT_NO_OUTPUT; if (diff_setup_done(&opts) < 0) die("diff setup failed"); @@ -1343,7 +1347,11 @@ static int merge_config(const char *var, const char *value) return 0; } if (!strcasecmp(var, "diff.renamelimit")) { - rename_limit = git_config_int(var, value); + diff_rename_limit = git_config_int(var, value); + return 0; + } + if (!strcasecmp(var, "merge.renamelimit")) { + merge_rename_limit = git_config_int(var, value); return 0; } return git_default_config(var, value); diff --git a/builtin-read-tree.c b/builtin-read-tree.c index e9cfd2bbc5..7ac30883bc 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -40,7 +40,7 @@ static int read_cache_unmerged(void) for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; if (ce_stage(ce)) { - remove_index_entry(ce); + remove_name_hash(ce); if (last && !strcmp(ce->name, last->name)) continue; cache_tree_invalidate_path(active_cache_tree, ce->name); diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index 0e59707323..f8d8548e9c 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -28,8 +28,6 @@ static int symbolic; static int abbrev; static int output_sq; -static int revs_count; - /* * Some arguments are relevant "revision" arguments, * others are about output format or other details. @@ -102,7 +100,6 @@ static void show_rev(int type, const unsigned char *sha1, const char *name) if (!(filter & DO_REVS)) return; def = NULL; - revs_count++; if (type != show_type) putchar('^'); @@ -150,7 +147,7 @@ static int show_flag(const char *arg) return 0; } -static void show_default(void) +static int show_default(void) { const char *s = def; @@ -160,9 +157,10 @@ static void show_default(void) def = NULL; if (!get_sha1(s, sha1)) { show_rev(NORMAL, sha1, s); - return; + return 1; } } + return 0; } static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data) @@ -375,8 +373,9 @@ static void die_no_single_rev(int quiet) int cmd_rev_parse(int argc, const char **argv, const char *prefix) { - int i, as_is = 0, verify = 0, quiet = 0; + int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0; unsigned char sha1[20]; + const char *name = NULL; if (argc > 1 && !strcmp("--parseopt", argv[1])) return cmd_parseopt(argc - 1, argv + 1, prefix); @@ -568,12 +567,17 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) /* Not a flag argument */ if (try_difference(arg)) continue; - if (!get_sha1(arg, sha1)) { - show_rev(NORMAL, sha1, arg); - continue; + name = arg; + type = NORMAL; + if (*arg == '^') { + name++; + type = REVERSED; } - if (*arg == '^' && !get_sha1(arg+1, sha1)) { - show_rev(REVERSED, sha1, arg+1); + if (!get_sha1(name, sha1)) { + if (verify) + revs_count++; + else + show_rev(type, sha1, name); continue; } if (verify) @@ -583,8 +587,14 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; verify_filename(prefix, arg); } - show_default(); - if (verify && revs_count != 1) + if (verify) { + if (revs_count == 1) { + show_rev(type, sha1, name); + return 0; + } else if (revs_count == 0 && show_default()) + return 0; die_no_single_rev(quiet); + } else + show_default(); return 0; } diff --git a/builtin-revert.c b/builtin-revert.c index 607a2f0337..2b57525d72 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -33,7 +33,7 @@ static const char * const cherry_pick_usage[] = { NULL }; -static int edit, no_replay, no_commit, mainline; +static int edit, no_replay, no_commit, mainline, signoff; static enum { REVERT, CHERRY_PICK } action; static struct commit *commit; @@ -53,6 +53,7 @@ static void parse_args(int argc, const char **argv) OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"), OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"), OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"), + OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"), OPT_INTEGER('m', "mainline", &mainline, "parent number"), OPT_END(), }; @@ -404,10 +405,19 @@ static int revert_or_cherry_pick(int argc, const char **argv) */ if (!no_commit) { - if (edit) - return execl_git_cmd("commit", "-n", NULL); - else - return execl_git_cmd("commit", "-n", "-F", defmsg, NULL); + /* 6 is max possible length of our args array including NULL */ + const char *args[6]; + int i = 0; + args[i++] = "commit"; + args[i++] = "-n"; + if (signoff) + args[i++] = "-s"; + if (!edit) { + args[i++] = "-F"; + args[i++] = defmsg; + } + args[i] = NULL; + return execv_git_cmd(args); } free(reencoded_message); @@ -133,6 +133,7 @@ struct cache_entry { #define CE_UPDATE (0x10000) #define CE_REMOVE (0x20000) #define CE_UPTODATE (0x40000) +#define CE_ADDED (0x80000) #define CE_HASHED (0x100000) #define CE_UNHASHED (0x200000) @@ -153,20 +154,6 @@ static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state; } -/* - * We don't actually *remove* it, we can just mark it invalid so that - * we won't find it in lookups. - * - * Not only would we have to search the lists (simple enough), but - * we'd also have to rehash other hash buckets in case this makes the - * hash bucket empty (common). So it's much better to just mark - * it. - */ -static inline void remove_index_entry(struct cache_entry *ce) -{ - ce->ce_flags |= CE_UNHASHED; -} - static inline unsigned create_ce_flags(size_t len, unsigned stage) { if (len >= CE_NAMEMASK) @@ -241,6 +228,23 @@ struct index_state { extern struct index_state the_index; +/* Name hashing */ +extern void add_name_hash(struct index_state *istate, struct cache_entry *ce); +/* + * We don't actually *remove* it, we can just mark it invalid so that + * we won't find it in lookups. + * + * Not only would we have to search the lists (simple enough), but + * we'd also have to rehash other hash buckets in case this makes the + * hash bucket empty (common). So it's much better to just mark + * it. + */ +static inline void remove_name_hash(struct cache_entry *ce) +{ + ce->ce_flags |= CE_UNHASHED; +} + + #ifndef NO_THE_INDEX_COMPATIBILITY_MACROS #define active_cache (the_index.cache) #define active_nr (the_index.cache_nr) @@ -257,11 +261,12 @@ extern struct index_state the_index; #define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option)) #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos)) #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path)) +#define add_to_cache(path, st, verbose) add_to_index(&the_index, (path), (st), (verbose)) #define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose)) #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL) #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options)) #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options)) -#define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen)) +#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase)) #endif enum object_type { @@ -351,7 +356,7 @@ extern int write_index(const 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); -extern int index_name_exists(struct index_state *istate, const char *name, int namelen); +extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase); extern int index_name_pos(const struct index_state *, const char *name, int namelen); #define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */ #define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */ @@ -361,6 +366,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 int remove_index_entry_at(struct index_state *, int pos); extern int remove_file_from_index(struct index_state *, const char *path); +extern int add_to_index(struct index_state *, const char *path, struct stat *, int verbose); extern int add_file_to_index(struct index_state *, const char *path, int verbose); extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh); extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); @@ -405,6 +411,7 @@ extern int delete_ref(const char *, const unsigned char *sha1); extern int trust_executable_bit; extern int quote_path_fully; extern int has_symlinks; +extern int ignore_case; extern int assume_unchanged; extern int prefer_symlink_refs; extern int log_all_ref_updates; @@ -434,7 +441,15 @@ enum branch_track { BRANCH_TRACK_EXPLICIT, }; +enum rebase_setup_type { + AUTOREBASE_NEVER = 0, + AUTOREBASE_LOCAL, + AUTOREBASE_REMOTE, + AUTOREBASE_ALWAYS, +}; + extern enum branch_track git_branch_track; +extern enum rebase_setup_type autorebase; #define GIT_REPO_VERSION 0 extern int repository_format_version; @@ -591,7 +606,7 @@ struct checkout { }; extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath); -extern int has_symlink_leading_path(const char *name, char *last_symlink); +extern int has_symlink_leading_path(int len, const char *name); extern struct alternate_object_database { struct alternate_object_database *next; @@ -635,6 +650,7 @@ struct ref { struct ref *next; unsigned char old_sha1[20]; unsigned char new_sha1[20]; + char *symref; unsigned int force:1, merge:1, nonfastforward:1, diff --git a/compat/fopen.c b/compat/fopen.c index ccb9e89fa4..b5ca142fed 100644 --- a/compat/fopen.c +++ b/compat/fopen.c @@ -1,5 +1,16 @@ +/* + * The order of the following two lines is important. + * + * FREAD_READS_DIRECTORIES is undefined before including git-compat-util.h + * to avoid the redefinition of fopen within git-compat-util.h. This is + * necessary since fopen is a macro on some platforms which may be set + * based on compiler options. For example, on AIX fopen is set to fopen64 + * when _LARGE_FILES is defined. The previous technique of merely undefining + * fopen after including git-compat-util.h is inadequate in this case. + */ +#undef FREAD_READS_DIRECTORIES #include "../git-compat-util.h" -#undef fopen + FILE *git_fopen(const char *path, const char *mode) { FILE *fp; @@ -350,6 +350,11 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.ignorecase")) { + ignore_case = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "core.bare")) { is_bare_repository_cfg = git_config_bool(var, value); return 0; @@ -491,6 +496,21 @@ int git_default_config(const char *var, const char *value) git_branch_track = git_config_bool(var, value); return 0; } + if (!strcmp(var, "branch.autosetuprebase")) { + if (!value) + return config_error_nonbool(var); + else if (!strcmp(value, "never")) + autorebase = AUTOREBASE_NEVER; + else if (!strcmp(value, "local")) + autorebase = AUTOREBASE_LOCAL; + else if (!strcmp(value, "remote")) + autorebase = AUTOREBASE_REMOTE; + else if (!strcmp(value, "always")) + autorebase = AUTOREBASE_ALWAYS; + else + return error("Malformed value for %s", var); + return 0; + } /* Add other config variables here and to Documentation/config.txt. */ return 0; @@ -611,11 +631,9 @@ static int store_aux(const char* key, const char* value) case KEY_SEEN: if (matches(key, value)) { if (store.seen == 1 && store.multi_replace == 0) { - fprintf(stderr, - "Warning: %s has multiple values\n", - key); + warning("%s has multiple values", key); } else if (store.seen >= MAX_MATCHES) { - fprintf(stderr, "Too many matches\n"); + error("too many matches for %s", key); return 1; } @@ -665,9 +683,9 @@ static int store_aux(const char* key, const char* value) return 0; } -static int write_error(void) +static int write_error(const char *filename) { - fprintf(stderr, "Failed to write new configuration file\n"); + error("failed to write new configuration file %s", filename); /* Same error code as "failed to rename". */ return 4; @@ -684,7 +702,7 @@ static int store_write_section(int fd, const char* key) if (dot) { strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key); for (i = dot - key + 1; i < store.baselen; i++) { - if (key[i] == '"') + if (key[i] == '"' || key[i] == '\\') strbuf_addch(&sb, '\\'); strbuf_addch(&sb, key[i]); } @@ -826,7 +844,7 @@ int git_config_set_multivar(const char* key, const char* value, */ if (last_dot == NULL) { - fprintf(stderr, "key does not contain a section: %s\n", key); + error("key does not contain a section: %s", key); ret = 2; goto out_free; } @@ -846,14 +864,14 @@ int git_config_set_multivar(const char* key, const char* value, /* Leave the extended basename untouched.. */ if (!dot || i > store.baselen) { if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) { - fprintf(stderr, "invalid key: %s\n", key); + error("invalid key: %s", key); free(store.key); ret = 1; goto out_free; } c = tolower(c); } else if (c == '\n') { - fprintf(stderr, "invalid key (newline): %s\n", key); + error("invalid key (newline): %s", key); free(store.key); ret = 1; goto out_free; @@ -869,7 +887,7 @@ int git_config_set_multivar(const char* key, const char* value, lock = xcalloc(sizeof(struct lock_file), 1); fd = hold_lock_file_for_update(lock, config_filename, 0); if (fd < 0) { - fprintf(stderr, "could not lock config file\n"); + error("could not lock config file %s", config_filename); free(store.key); ret = -1; goto out_free; @@ -916,8 +934,7 @@ int git_config_set_multivar(const char* key, const char* value, store.value_regex = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(store.value_regex, value_regex, REG_EXTENDED)) { - fprintf(stderr, "Invalid pattern: %s\n", - value_regex); + error("invalid pattern: %s", value_regex); free(store.value_regex); ret = 6; goto out_free; @@ -935,7 +952,7 @@ int git_config_set_multivar(const char* key, const char* value, * existing config file. */ if (git_config_from_file(store_aux, config_filename)) { - fprintf(stderr, "invalid config file\n"); + error("invalid config file %s", config_filename); free(store.key); if (store.value_regex != NULL) { regfree(store.value_regex); @@ -1014,7 +1031,7 @@ int git_config_set_multivar(const char* key, const char* value, } if (commit_lock_file(lock) < 0) { - fprintf(stderr, "Cannot commit config file!\n"); + error("could not commit config file %s", config_filename); ret = 4; goto out_free; } @@ -1035,7 +1052,7 @@ out_free: return ret; write_err_out: - ret = write_error(); + ret = write_error(lock->filename); goto out_free; } @@ -1085,7 +1102,7 @@ int git_config_rename_section(const char *old_name, const char *new_name) config_filename = xstrdup(config_filename); out_fd = hold_lock_file_for_update(lock, config_filename, 0); if (out_fd < 0) { - ret = error("Could not lock config file!"); + ret = error("could not lock config file %s", config_filename); goto out; } @@ -1109,7 +1126,7 @@ int git_config_rename_section(const char *old_name, const char *new_name) } store.baselen = strlen(new_name); if (!store_write_section(out_fd, new_name)) { - ret = write_error(); + ret = write_error(lock->filename); goto out; } continue; @@ -1120,14 +1137,14 @@ int git_config_rename_section(const char *old_name, const char *new_name) continue; length = strlen(buf); if (write_in_full(out_fd, buf, length) != length) { - ret = write_error(); + ret = write_error(lock->filename); goto out; } } fclose(config_file); unlock_and_out: if (commit_lock_file(lock) < 0) - ret = error("Cannot commit config file!"); + ret = error("could not commit config file %s", config_filename); out: free(config_filename); return ret; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 23db664f48..16984632d9 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -780,7 +780,7 @@ _git_merge () ;; --*) __gitcomp " - --no-commit --no-summary --squash --strategy + --no-commit --no-stat --log --no-log --squash --strategy " return esac diff --git a/diff-lib.c b/diff-lib.c index 9139e45fb9..fe2ccec7e6 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -337,22 +337,41 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv) } return run_diff_files(revs, options); } + /* - * See if work tree has an entity that can be staged. Return 0 if so, - * return 1 if not and return -1 if error. + * Has the work tree entity been removed? + * + * Return 1 if it was removed from the work tree, 0 if an entity to be + * compared with the cache entry ce still exists (the latter includes + * the case where a directory that is not a submodule repository + * exists for ce that is a submodule -- it is a submodule that is not + * checked out). Return negative for an error. */ -static int check_work_tree_entity(const struct cache_entry *ce, struct stat *st, char *symcache) +static int check_removed(const struct cache_entry *ce, struct stat *st) { if (lstat(ce->name, st) < 0) { if (errno != ENOENT && errno != ENOTDIR) return -1; return 1; } - if (has_symlink_leading_path(ce->name, symcache)) + if (has_symlink_leading_path(ce_namelen(ce), ce->name)) return 1; if (S_ISDIR(st->st_mode)) { unsigned char sub[20]; - if (resolve_gitlink_ref(ce->name, "HEAD", sub)) + + /* + * If ce is already a gitlink, we can have a plain + * directory (i.e. the submodule is not checked out), + * or a checked out submodule. Either case this is not + * a case where something was removed from the work tree, + * so we will return 0. + * + * Otherwise, if the directory is not a submodule + * repository, that means ce which was a blob turned into + * a directory --- the blob was removed! + */ + if (!S_ISGITLINK(ce->ce_mode) && + resolve_gitlink_ref(ce->name, "HEAD", sub)) return 1; } return 0; @@ -402,7 +421,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) memset(&(dpath->parent[0]), 0, sizeof(struct combine_diff_parent)*5); - changed = check_work_tree_entity(ce, &st, symcache); + changed = check_removed(ce, &st); if (!changed) dpath->mode = ce_mode_from_stat(ce, st.st_mode); else { @@ -466,7 +485,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) if (ce_uptodate(ce)) continue; - changed = check_work_tree_entity(ce, &st, symcache); + changed = check_removed(ce, &st); if (changed) { if (changed < 0) { perror(ce->name); @@ -527,7 +546,7 @@ static int get_stat_data(struct cache_entry *ce, if (!cached) { int changed; struct stat st; - changed = check_work_tree_entity(ce, &st, cbdata->symcache); + changed = check_removed(ce, &st); if (changed < 0) return -1; else if (changed) { @@ -19,7 +19,7 @@ #endif static int diff_detect_rename_default; -static int diff_rename_limit_default = 100; +static int diff_rename_limit_default = 200; int diff_use_color_default = -1; static const char *external_diff_cmd_cfg; int diff_auto_refresh_index = 1; @@ -83,6 +83,7 @@ struct diff_options { int pickaxe_opts; int rename_score; int rename_limit; + int warn_on_too_large_rename; int dirstat_percent; int setup; int abbrev; diff --git a/diffcore-rename.c b/diffcore-rename.c index 1369a5ec45..1b2ebb4001 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -492,7 +492,8 @@ void diffcore_rename(struct diff_options *options) rename_limit = 32767; if ((num_create > rename_limit && num_src > rename_limit) || (num_create * num_src > rename_limit * rename_limit)) { - warning("too many files, skipping inexact rename detection"); + if (options->warn_on_too_large_rename) + warning("too many files, skipping inexact rename detection"); goto cleanup; } @@ -52,6 +52,11 @@ int common_prefix(const char **pathspec) return prefix; } +static inline int special_char(unsigned char c1) +{ + return !c1 || c1 == '*' || c1 == '[' || c1 == '?'; +} + /* * Does 'match' matches the given name? * A match is found if @@ -69,14 +74,27 @@ static int match_one(const char *match, const char *name, int namelen) int matchlen; /* If the match was just the prefix, we matched */ - matchlen = strlen(match); - if (!matchlen) + if (!*match) return MATCHED_RECURSIVELY; + for (;;) { + unsigned char c1 = *match; + unsigned char c2 = *name; + if (special_char(c1)) + break; + if (c1 != c2) + return 0; + match++; + name++; + namelen--; + } + + /* * If we don't match the matchstring exactly, * we need to match by fnmatch */ + matchlen = strlen(match); if (strncmp(match, name, matchlen)) return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0; @@ -371,7 +389,7 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len) struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len) { - if (cache_name_exists(pathname, len)) + if (cache_name_exists(pathname, len, ignore_case)) return NULL; ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc); diff --git a/environment.c b/environment.c index 1b80036ca5..4fcb471248 100644 --- a/environment.c +++ b/environment.c @@ -15,6 +15,7 @@ int user_ident_explicitly_given; int trust_executable_bit = 1; int quote_path_fully = 1; int has_symlinks = 1; +int ignore_case; int assume_unchanged; int prefer_symlink_refs; int is_bare_repository_cfg = -1; /* unspecified */ @@ -39,6 +40,7 @@ int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ enum safe_crlf safe_crlf = SAFE_CRLF_WARN; unsigned whitespace_rule_cfg = WS_DEFAULT_RULE; enum branch_track git_branch_track = BRANCH_TRACK_REMOTE; +enum rebase_setup_type autorebase = AUTOREBASE_NEVER; /* This is set by setup_git_dir_gently() and/or git_default_config() */ char *git_work_tree_cfg; diff --git a/git-bisect.sh b/git-bisect.sh index d8d9bfde4c..164e8ed81f 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -69,14 +69,19 @@ bisect_start() { head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) || head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) || die "Bad HEAD - I need a HEAD" + # + # Check that we either already have BISECT_START, or that the + # branches bisect, new-bisect don't exist, to not override them. + # + test -s "$GIT_DIR/BISECT_START" || + if git show-ref --verify -q refs/heads/bisect || + git show-ref --verify -q refs/heads/new-bisect; then + die 'The branches "bisect" and "new-bisect" must not exist.' + fi start_head='' case "$head" in refs/heads/bisect) - if [ -s "$GIT_DIR/BISECT_START" ]; then - branch=`cat "$GIT_DIR/BISECT_START"` - else - branch=master - fi + branch=`cat "$GIT_DIR/BISECT_START"` git checkout $branch || exit ;; refs/heads/*|$_x40) @@ -219,18 +224,33 @@ bisect_auto_next() { bisect_next_check && bisect_next || : } +eval_rev_list() { + _eval="$1" + + eval $_eval + res=$? + + if [ $res -ne 0 ]; then + echo >&2 "'git rev-list --bisect-vars' failed:" + echo >&2 "maybe you mistake good and bad revs?" + exit $res + fi + + return $res +} + filter_skipped() { _eval="$1" _skip="$2" if [ -z "$_skip" ]; then - eval $_eval + eval_rev_list "$_eval" return fi # Let's parse the output of: # "git rev-list --bisect-vars --bisect-all ..." - eval $_eval | while read hash line + eval_rev_list "$_eval" | while read hash line do case "$VARS,$FOUND,$TRIED,$hash" in # We display some vars. @@ -328,8 +348,8 @@ bisect_next() { exit_if_skipped_commits "$bisect_rev" echo "Bisecting: $bisect_nr revisions left to test after this" - git branch -f new-bisect "$bisect_rev" - git checkout -q new-bisect || exit + git branch -D new-bisect 2> /dev/null + git checkout -q -b new-bisect "$bisect_rev" || exit git branch -M new-bisect bisect git show-branch "$bisect_rev" } diff --git a/git-clone.sh b/git-clone.sh index 8c7fc7f631..547228e13c 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -240,7 +240,6 @@ die "working tree '$GIT_WORK_TREE' already exists." D= W= cleanup() { - err=$? test -z "$D" && rm -rf "$dir" test -z "$W" && test -n "$GIT_WORK_TREE" && rm -rf "$GIT_WORK_TREE" cd .. @@ -248,7 +247,7 @@ cleanup() { test -n "$W" && rm -rf "$W" exit $err } -trap cleanup 0 +trap 'err=$?; cleanup' 0 mkdir -p "$dir" && D=$(cd "$dir" && pwd) || usage test -n "$GIT_WORK_TREE" && mkdir -p "$GIT_WORK_TREE" && W=$(cd "$GIT_WORK_TREE" && pwd) && GIT_WORK_TREE="$W" && export GIT_WORK_TREE @@ -334,7 +333,10 @@ yes) fi fi && cd "$repo" && - find objects -depth -print | cpio $cpio_quiet_flag -pumd$l "$GIT_DIR/" || \ + # Create dirs using umask and permissions and destination + find objects -type d -print | (cd "$GIT_DIR" && xargs mkdir -p) && + # Copy existing 0444 permissions on content + find objects ! -type d -print | cpio $cpio_quiet_flag -pumd$l "$GIT_DIR/" || \ exit 1 fi git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1 diff --git a/git-compat-util.h b/git-compat-util.h index 167c3fe63a..01c4045e89 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -206,6 +206,9 @@ void *gitmemmem(const void *haystack, size_t haystacklen, #endif #ifdef FREAD_READS_DIRECTORIES +#ifdef fopen +#undef fopen +#endif #define fopen(a,b) git_fopen(a,b) extern FILE *git_fopen(const char*, const char*); #endif diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 333f6a8f3b..80e99e5394 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -435,11 +435,17 @@ rm -rf "$tempdir" trap - 0 unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE -test -z "$ORIG_GIT_DIR" || GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR -test -z "$ORIG_GIT_WORK_TREE" || GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" && +test -z "$ORIG_GIT_DIR" || { + GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR +} +test -z "$ORIG_GIT_WORK_TREE" || { + GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" && export GIT_WORK_TREE -test -z "$ORIG_GIT_INDEX_FILE" || GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" && +} +test -z "$ORIG_GIT_INDEX_FILE" || { + GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" && export GIT_INDEX_FILE +} git read-tree -u -m HEAD exit $ret diff --git a/git-merge.sh b/git-merge.sh index 7dbbb1d79d..69b35d87e6 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -8,8 +8,12 @@ OPTIONS_SPEC="\ git-merge [options] <remote>... git-merge [options] <msg> HEAD <remote> -- -summary show a diffstat at the end of the merge -n,no-summary don't show a diffstat at the end of the merge +stat show a diffstat at the end of the merge +n,no-stat don't show a diffstat at the end of the merge +summary (synonym to --stat) +no-summary (synonym to --no-stat) +log add list of one-line log to merge commit message +no-log don't add list of one-line log to merge commit message squash create a single commit instead of doing a merge commit perform a commit if the merge sucesses (default) ff allow fast forward (default) @@ -37,7 +41,7 @@ use_strategies= allow_fast_forward=t allow_trivial_merge=t -squash= no_commit= +squash= no_commit= log_arg= dropsave() { rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \ @@ -148,10 +152,12 @@ merge_name () { parse_config () { while test $# != 0; do case "$1" in - -n|--no-summary) + -n|--no-stat|--no-summary) show_diffstat=false ;; - --summary) + --stat|--summary) show_diffstat=t ;; + --log|--no-log) + log_arg=$1 ;; --squash) test "$allow_fast_forward" = t || die "You cannot combine --squash with --no-ff." @@ -210,6 +216,7 @@ while test $args_left -lt $#; do shift; done if test -z "$show_diffstat"; then test "$(git config --bool merge.diffstat)" = false && show_diffstat=false + test "$(git config --bool merge.stat)" = false && show_diffstat=false test -z "$show_diffstat" && show_diffstat=t fi @@ -258,7 +265,7 @@ else merge_name=$(for remote do merge_name "$remote" - done | git fmt-merge-msg + done | git fmt-merge-msg $log_arg ) merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name" fi diff --git a/git-mergetool.sh b/git-mergetool.sh index 5c86f69229..fcdec4a504 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -237,9 +237,9 @@ merge_file () { ecmerge) touch "$BACKUP" if base_present; then - "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$MERGED" + "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --default --mode=merge3 --to="$MERGED" else - "$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$MERGED" + "$merge_tool_path" "$LOCAL" "$REMOTE" --default --mode=merge2 --to="$MERGED" fi check_unchanged ;; diff --git a/git-pull.sh b/git-pull.sh index 3ce32b5f21..bf0c2985af 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -4,7 +4,7 @@ # # Fetch one or more remote refs and merge it/them into the current HEAD. -USAGE='[-n | --no-summary] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...' +USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...' LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.' SUBDIRECTORY_OK=Yes OPTIONS_SPEC= @@ -16,19 +16,19 @@ cd_to_toplevel test -z "$(git ls-files -u)" || die "You are in the middle of a conflicted merge." -strategy_args= no_summary= no_commit= squash= no_ff= +strategy_args= no_stat= no_commit= squash= no_ff= log_arg= curr_branch=$(git symbolic-ref -q HEAD) curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||") rebase=$(git config --bool branch.$curr_branch_short.rebase) while : do case "$1" in - -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\ - --no-summa|--no-summar|--no-summary) - no_summary=-n ;; - --summary) - no_summary=$1 - ;; + -n|--no-stat|--no-summary) + no_stat=-n ;; + --stat|--summary) + no_stat=$1 ;; + --log|--no-log) + log_arg=$1 ;; --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit) no_commit=--no-commit ;; --c|--co|--com|--comm|--commi|--commit) @@ -172,9 +172,9 @@ then exit fi -merge_name=$(git fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit +merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit test true = "$rebase" && exec git-rebase $strategy_args --onto $merge_head \ ${oldremoteref:-$merge_head} -exec git-merge $no_summary $no_commit $squash $no_ff $strategy_args \ +exec git-merge $no_stat $no_commit $squash $no_ff $log_arg $strategy_args \ "$merge_name" HEAD $merge_head diff --git a/git-rebase.sh b/git-rebase.sh index 9b13b833cb..68855c18ae 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -214,7 +214,7 @@ do else die "No rebase in progress?" fi - git reset --hard $(cat $dotest/orig-head) + git reset --hard $(cat "$dotest/orig-head") rm -r "$dotest" exit ;; @@ -353,7 +353,7 @@ orig_head=$branch mb=$(git merge-base "$onto" "$branch") if test "$upstream" = "$onto" && test "$mb" = "$onto" && # linear history? - ! git rev-list --parents "$onto".."$branch" | grep " .* " > /dev/null + ! (git rev-list --parents "$onto".."$branch" | grep " .* ") > /dev/null then # Lazily switch to the target branch if needed... test -z "$switch_to" || git checkout "$switch_to" diff --git a/git-repack.sh b/git-repack.sh index e18eb3f5dc..501519ab68 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -11,6 +11,7 @@ a pack everything in a single pack A same as -a, and keep unreachable objects too d remove redundant packs, and run git-prune-packed f pass --no-reuse-delta to git-pack-objects +n do not run git-update-server-info q,quiet be quiet l pass --local to git-pack-objects Packing constraints diff --git a/git-send-email.perl b/git-send-email.perl index 9e568bf9c0..1e1d98656d 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -203,6 +203,7 @@ my %config_settings = ( "smtpuser" => \$smtp_authuser, "smtppass" => \$smtp_authpass, "to" => \@to, + "cc" => \@initial_cc, "cccmd" => \$cc_cmd, "aliasfiletype" => \$aliasfiletype, "bcc" => \@bcclist, @@ -512,7 +513,7 @@ EOT close(C); my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; - system('sh', '-c', '$0 $@', $editor, $compose_filename); + system('sh', '-c', $editor.' "$@"', $editor, $compose_filename); open(C2,">",$compose_filename . ".final") or die "Failed to open $compose_filename.final : " . $!; diff --git a/git-svn.perl b/git-svn.perl index e47b1ea6c1..2c53f39aef 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -65,7 +65,8 @@ my ($_stdin, $_help, $_edit, $_template, $_shared, $_version, $_fetch_all, $_no_rebase, $_merge, $_strategy, $_dry_run, $_local, - $_prefix, $_no_checkout, $_url, $_verbose); + $_prefix, $_no_checkout, $_url, $_verbose, + $_git_format); $Git::SVN::_follow_parent = 1; my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username, 'config-dir=s' => \$Git::SVN::Ra::config_dir, @@ -188,7 +189,7 @@ my %cmd = ( { 'url' => \$_url, } ], 'blame' => [ \&Git::SVN::Log::cmd_blame, "Show what revision and author last modified each line of a file", - {} ], + { 'git-format' => \$_git_format } ], ); my $cmd; @@ -225,7 +226,7 @@ unless ($cmd && $cmd =~ /(?:clone|init|multi-init)$/) { my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd); read_repo_config(\%opts); -Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log'); +Getopt::Long::Configure('pass_through') if ($cmd && ($cmd eq 'log' || $cmd eq 'blame')); my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version, 'minimize-connections' => \$Git::SVN::Migration::_minimize, 'id|i=s' => \$Git::SVN::default_ref_id, @@ -3673,7 +3674,7 @@ sub escape_uri_only { my ($uri) = @_; my @tmp; foreach (split m{/}, $uri) { - s/([^\w.%-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg; + s/([^\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg; push @tmp, $_; } join('/', @tmp); @@ -4468,19 +4469,51 @@ out: } sub cmd_blame { - my $path = shift; + my $path = pop; config_pager(); run_pager(); - my ($fh, $ctx) = command_output_pipe('blame', @_, $path); - while (my $line = <$fh>) { - if ($line =~ /^\^?([[:xdigit:]]+)\s/) { - my (undef, $rev, undef) = ::cmt_metadata($1); - $rev = sprintf('%-10s', $rev); - $line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/; + my ($fh, $ctx, $rev); + + if ($_git_format) { + ($fh, $ctx) = command_output_pipe('blame', @_, $path); + while (my $line = <$fh>) { + if ($line =~ /^\^?([[:xdigit:]]+)\s/) { + # Uncommitted edits show up as a rev ID of + # all zeros, which we can't look up with + # cmt_metadata + if ($1 !~ /^0+$/) { + (undef, $rev, undef) = + ::cmt_metadata($1); + $rev = '0' if (!$rev); + } else { + $rev = '0'; + } + $rev = sprintf('%-10s', $rev); + $line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/; + } + print $line; + } + } else { + ($fh, $ctx) = command_output_pipe('blame', '-p', @_, 'HEAD', + '--', $path); + my ($sha1); + my %authors; + while (my $line = <$fh>) { + if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) { + $sha1 = $1; + (undef, $rev, undef) = ::cmt_metadata($1); + $rev = '0' if (!$rev); + } + elsif ($line =~ /^author (.*)/) { + $authors{$rev} = $1; + $authors{$rev} =~ s/\s/_/g; + } + elsif ($line =~ /^\t(.*)$/) { + printf("%6s %10s %s\n", $rev, $authors{$rev}, $1); + } } - print $line; } command_close_pipe($fh, $ctx); } diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index 446a1c333b..aa0eeca247 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -464,6 +464,14 @@ a.rss_logo:hover { background-color: #ee5500; } +a.rss_logo.generic { + background-color: #ff8800; +} + +a.rss_logo.generic:hover { + background-color: #ee7700; +} + span.refs span { padding: 0px 4px; font-size: 70%; diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index f83567ec39..2facf2db7a 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -592,7 +592,7 @@ exit; ## ====================================================================== ## action links -sub href(%) { +sub href (%) { my %params = @_; # default is to use -absolute url() i.e. $my_uri my $href = $params{-full} ? $my_url : $my_uri; @@ -1448,6 +1448,46 @@ sub format_snapshot_links { } } +## ...................................................................... +## functions returning values to be passed, perhaps after some +## transformation, to other functions; e.g. returning arguments to href() + +# returns hash to be passed to href to generate gitweb URL +# in -title key it returns description of link +sub get_feed_info { + my $format = shift || 'Atom'; + my %res = (action => lc($format)); + + # feed links are possible only for project views + return unless (defined $project); + # some views should link to OPML, or to generic project feed, + # or don't have specific feed yet (so they should use generic) + return if ($action =~ /^(?:tags|heads|forks|tag|search)$/x); + + my $branch; + # branches refs uses 'refs/heads/' prefix (fullname) to differentiate + # from tag links; this also makes possible to detect branch links + if ((defined $hash_base && $hash_base =~ m!^refs/heads/(.*)$!) || + (defined $hash && $hash =~ m!^refs/heads/(.*)$!)) { + $branch = $1; + } + # find log type for feed description (title) + my $type = 'log'; + if (defined $file_name) { + $type = "history of $file_name"; + $type .= "/" if ($action eq 'tree'); + $type .= " on '$branch'" if (defined $branch); + } else { + $type = "log of $branch" if (defined $branch); + } + + $res{-title} = $type; + $res{'hash'} = (defined $branch ? "refs/heads/$branch" : undef); + $res{'file_name'} = $file_name; + + return %res; +} + ## ---------------------------------------------------------------------- ## git utility subroutines, invoking git commands @@ -2510,30 +2550,49 @@ EOF } } if (defined $project) { - printf('<link rel="alternate" title="%s log RSS feed" '. - 'href="%s" type="application/rss+xml" />'."\n", - esc_param($project), href(action=>"rss")); - printf('<link rel="alternate" title="%s log RSS feed (no merges)" '. - 'href="%s" type="application/rss+xml" />'."\n", - esc_param($project), href(action=>"rss", - extra_options=>"--no-merges")); - printf('<link rel="alternate" title="%s log Atom feed" '. - 'href="%s" type="application/atom+xml" />'."\n", - esc_param($project), href(action=>"atom")); - printf('<link rel="alternate" title="%s log Atom feed (no merges)" '. - 'href="%s" type="application/atom+xml" />'."\n", - esc_param($project), href(action=>"atom", - extra_options=>"--no-merges")); + my %href_params = get_feed_info(); + if (!exists $href_params{'-title'}) { + $href_params{'-title'} = 'log'; + } + + foreach my $format qw(RSS Atom) { + my $type = lc($format); + my %link_attr = ( + '-rel' => 'alternate', + '-title' => "$project - $href_params{'-title'} - $format feed", + '-type' => "application/$type+xml" + ); + + $href_params{'action'} = $type; + $link_attr{'-href'} = href(%href_params); + print "<link ". + "rel=\"$link_attr{'-rel'}\" ". + "title=\"$link_attr{'-title'}\" ". + "href=\"$link_attr{'-href'}\" ". + "type=\"$link_attr{'-type'}\" ". + "/>\n"; + + $href_params{'extra_options'} = '--no-merges'; + $link_attr{'-href'} = href(%href_params); + $link_attr{'-title'} .= ' (no merges)'; + print "<link ". + "rel=\"$link_attr{'-rel'}\" ". + "title=\"$link_attr{'-title'}\" ". + "href=\"$link_attr{'-href'}\" ". + "type=\"$link_attr{'-type'}\" ". + "/>\n"; + } + } else { printf('<link rel="alternate" title="%s projects list" '. - 'href="%s" type="text/plain; charset=utf-8"/>'."\n", + 'href="%s" type="text/plain; charset=utf-8" />'."\n", $site_name, href(project=>undef, action=>"project_index")); printf('<link rel="alternate" title="%s projects feeds" '. - 'href="%s" type="text/x-opml"/>'."\n", + 'href="%s" type="text/x-opml" />'."\n", $site_name, href(project=>undef, action=>"opml")); } if (defined $favicon) { - print qq(<link rel="shortcut icon" href="$favicon" type="image/png"/>\n); + print qq(<link rel="shortcut icon" href="$favicon" type="image/png" />\n); } print "</head>\n" . @@ -2601,23 +2660,35 @@ EOF } sub git_footer_html { + my $feed_class = 'rss_logo'; + print "<div class=\"page_footer\">\n"; if (defined $project) { my $descr = git_get_project_description($project); if (defined $descr) { print "<div class=\"page_footer_text\">" . esc_html($descr) . "</div>\n"; } - print $cgi->a({-href => href(action=>"rss"), - -class => "rss_logo"}, "RSS") . " "; - print $cgi->a({-href => href(action=>"atom"), - -class => "rss_logo"}, "Atom") . "\n"; + + my %href_params = get_feed_info(); + if (!%href_params) { + $feed_class .= ' generic'; + } + $href_params{'-title'} ||= 'log'; + + foreach my $format qw(RSS Atom) { + $href_params{'action'} = lc($format); + print $cgi->a({-href => href(%href_params), + -title => "$href_params{'-title'} $format feed", + -class => $feed_class}, $format)."\n"; + } + } else { print $cgi->a({-href => href(project=>undef, action=>"opml"), - -class => "rss_logo"}, "OPML") . " "; + -class => $feed_class}, "OPML") . " "; print $cgi->a({-href => href(project=>undef, action=>"project_index"), - -class => "rss_logo"}, "TXT") . "\n"; + -class => $feed_class}, "TXT") . "\n"; } - print "</div>\n" ; + print "</div>\n"; # class="page_footer" if (-f $site_footer) { open (my $fd, $site_footer); @@ -11,10 +11,16 @@ #include "run-command.h" static struct man_viewer_list { - void (*exec)(const char *); struct man_viewer_list *next; + char name[FLEX_ARRAY]; } *man_viewer_list; +static struct man_viewer_info_list { + struct man_viewer_info_list *next; + const char *info; + char name[FLEX_ARRAY]; +} *man_viewer_info_list; + enum help_format { HELP_FORMAT_MAN, HELP_FORMAT_INFO, @@ -49,6 +55,18 @@ static enum help_format parse_help_format(const char *format) die("unrecognized help format '%s'", format); } +static const char *get_man_viewer_info(const char *name) +{ + struct man_viewer_info_list *viewer; + + for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) + { + if (!strcasecmp(name, viewer->name)) + return viewer->info; + } + return NULL; +} + static int check_emacsclient_version(void) { struct strbuf buffer = STRBUF_INIT; @@ -95,56 +113,145 @@ static int check_emacsclient_version(void) return 0; } -static void exec_woman_emacs(const char *page) +static void exec_woman_emacs(const char* path, const char *page) { if (!check_emacsclient_version()) { /* This works only with emacsclient version >= 22. */ struct strbuf man_page = STRBUF_INIT; + + if (!path) + path = "emacsclient"; strbuf_addf(&man_page, "(woman \"%s\")", page); - execlp("emacsclient", "emacsclient", "-e", man_page.buf, NULL); + execlp(path, "emacsclient", "-e", man_page.buf, NULL); + warning("failed to exec '%s': %s", path, strerror(errno)); } } -static void exec_man_konqueror(const char *page) +static void exec_man_konqueror(const char* path, const char *page) { const char *display = getenv("DISPLAY"); if (display && *display) { struct strbuf man_page = STRBUF_INIT; + const char *filename = "kfmclient"; + + /* It's simpler to launch konqueror using kfmclient. */ + if (path) { + const char *file = strrchr(path, '/'); + if (file && !strcmp(file + 1, "konqueror")) { + char *new = xstrdup(path); + char *dest = strrchr(new, '/'); + + /* strlen("konqueror") == strlen("kfmclient") */ + strcpy(dest + 1, "kfmclient"); + path = new; + } + if (file) + filename = file; + } else + path = "kfmclient"; strbuf_addf(&man_page, "man:%s(1)", page); - execlp("kfmclient", "kfmclient", "newTab", man_page.buf, NULL); + execlp(path, filename, "newTab", man_page.buf, NULL); + warning("failed to exec '%s': %s", path, strerror(errno)); } } -static void exec_man_man(const char *page) +static void exec_man_man(const char* path, const char *page) +{ + if (!path) + path = "man"; + execlp(path, "man", page, NULL); + warning("failed to exec '%s': %s", path, strerror(errno)); +} + +static void exec_man_cmd(const char *cmd, const char *page) { - execlp("man", "man", page, NULL); + struct strbuf shell_cmd = STRBUF_INIT; + strbuf_addf(&shell_cmd, "%s %s", cmd, page); + execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL); + warning("failed to exec '%s': %s", cmd, strerror(errno)); } -static void do_add_man_viewer(void (*exec)(const char *)) +static void add_man_viewer(const char *name) { struct man_viewer_list **p = &man_viewer_list; + size_t len = strlen(name); while (*p) p = &((*p)->next); - *p = xmalloc(sizeof(**p)); - (*p)->next = NULL; - (*p)->exec = exec; + *p = xcalloc(1, (sizeof(**p) + len + 1)); + strncpy((*p)->name, name, len); } -static int add_man_viewer(const char *value) +static int supported_man_viewer(const char *name, size_t len) { - if (!strcasecmp(value, "man")) - do_add_man_viewer(exec_man_man); - else if (!strcasecmp(value, "woman")) - do_add_man_viewer(exec_woman_emacs); - else if (!strcasecmp(value, "konqueror")) - do_add_man_viewer(exec_man_konqueror); + return (!strncasecmp("man", name, len) || + !strncasecmp("woman", name, len) || + !strncasecmp("konqueror", name, len)); +} + +static void do_add_man_viewer_info(const char *name, + size_t len, + const char *value) +{ + struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1); + + strncpy(new->name, name, len); + new->info = xstrdup(value); + new->next = man_viewer_info_list; + man_viewer_info_list = new; +} + +static int add_man_viewer_path(const char *name, + size_t len, + const char *value) +{ + if (supported_man_viewer(name, len)) + do_add_man_viewer_info(name, len, value); else - warning("'%s': unsupported man viewer.", value); + warning("'%s': path for unsupported man viewer.\n" + "Please consider using 'man.<tool>.cmd' instead.", + name); return 0; } +static int add_man_viewer_cmd(const char *name, + size_t len, + const char *value) +{ + if (supported_man_viewer(name, len)) + warning("'%s': cmd for supported man viewer.\n" + "Please consider using 'man.<tool>.path' instead.", + name); + else + do_add_man_viewer_info(name, len, value); + + return 0; +} + +static int add_man_viewer_info(const char *var, const char *value) +{ + const char *name = var + 4; + const char *subkey = strrchr(name, '.'); + + if (!subkey) + return error("Config with no key for man viewer: %s", name); + + if (!strcmp(subkey, ".path")) { + if (!value) + return config_error_nonbool(var); + return add_man_viewer_path(name, subkey - name, value); + } + if (!strcmp(subkey, ".cmd")) { + if (!value) + return config_error_nonbool(var); + return add_man_viewer_cmd(name, subkey - name, value); + } + + warning("'%s': unsupported man viewer sub key.", subkey); + return 0; +} + static int git_help_config(const char *var, const char *value) { if (!strcmp(var, "help.format")) { @@ -156,8 +263,12 @@ static int git_help_config(const char *var, const char *value) if (!strcmp(var, "man.viewer")) { if (!value) return config_error_nonbool(var); - return add_man_viewer(value); + add_man_viewer(value); + return 0; } + if (!prefixcmp(var, "man.")) + return add_man_viewer_info(var, value); + return git_default_config(var, value); } @@ -453,6 +564,22 @@ static void setup_man_path(void) strbuf_release(&new_path); } +static void exec_viewer(const char *name, const char *page) +{ + const char *info = get_man_viewer_info(name); + + if (!strcasecmp(name, "man")) + exec_man_man(info, page); + else if (!strcasecmp(name, "woman")) + exec_woman_emacs(info, page); + else if (!strcasecmp(name, "konqueror")) + exec_man_konqueror(info, page); + else if (info) + exec_man_cmd(info, page); + else + warning("'%s': unknown man viewer.", name); +} + static void show_man_page(const char *git_cmd) { struct man_viewer_list *viewer; @@ -461,9 +588,9 @@ static void show_man_page(const char *git_cmd) setup_man_path(); for (viewer = man_viewer_list; viewer; viewer = viewer->next) { - viewer->exec(page); /* will return when unable */ + exec_viewer(viewer->name, page); /* will return when unable */ } - exec_man_man(page); + exec_viewer("man", page); die("no man viewer handled the request"); } diff --git a/http-push.c b/http-push.c index 5b230380cc..42727c8a45 100644 --- a/http-push.c +++ b/http-push.c @@ -1759,15 +1759,15 @@ static int one_local_ref(const char *refname, const unsigned char *sha1, int fla static void one_remote_ref(char *refname) { struct ref *ref; - unsigned char remote_sha1[20]; struct object *obj; - int len = strlen(refname) + 1; - if (http_fetch_ref(remote->url, refname + 5 /* "refs/" */, - remote_sha1) != 0) { + ref = alloc_ref_from_str(refname); + + if (http_fetch_ref(remote->url, ref) != 0) { fprintf(stderr, "Unable to fetch ref %s from %s\n", refname, remote->url); + free(ref); return; } @@ -1775,18 +1775,15 @@ static void one_remote_ref(char *refname) * Fetch a copy of the object if it doesn't exist locally - it * may be required for updating server info later. */ - if (remote->can_update_info_refs && !has_sha1_file(remote_sha1)) { - obj = lookup_unknown_object(remote_sha1); + if (remote->can_update_info_refs && !has_sha1_file(ref->old_sha1)) { + obj = lookup_unknown_object(ref->old_sha1); if (obj) { fprintf(stderr, " fetch %s for %s\n", - sha1_to_hex(remote_sha1), refname); + sha1_to_hex(ref->old_sha1), refname); add_fetch_request(obj); } } - ref = xcalloc(1, sizeof(*ref) + len); - hashcpy(ref->old_sha1, remote_sha1); - memcpy(ref->name, refname, len); *remote_tail = ref; remote_tail = &ref->next; } @@ -1891,33 +1888,36 @@ static void mark_edges_uninteresting(struct commit_list *list) static void add_remote_info_ref(struct remote_ls_ctx *ls) { struct strbuf *buf = (struct strbuf *)ls->userData; - unsigned char remote_sha1[20]; struct object *o; int len; char *ref_info; + struct ref *ref; + + ref = alloc_ref_from_str(ls->dentry_name); - if (http_fetch_ref(remote->url, ls->dentry_name + 5 /* "refs/" */, - remote_sha1) != 0) { + if (http_fetch_ref(remote->url, ref) != 0) { fprintf(stderr, "Unable to fetch ref %s from %s\n", ls->dentry_name, remote->url); aborted = 1; + free(ref); return; } - o = parse_object(remote_sha1); + o = parse_object(ref->old_sha1); if (!o) { fprintf(stderr, "Unable to parse object %s for remote ref %s\n", - sha1_to_hex(remote_sha1), ls->dentry_name); + sha1_to_hex(ref->old_sha1), ls->dentry_name); aborted = 1; + free(ref); return; } len = strlen(ls->dentry_name) + 42; ref_info = xcalloc(len + 1, 1); sprintf(ref_info, "%s %s\n", - sha1_to_hex(remote_sha1), ls->dentry_name); + sha1_to_hex(ref->old_sha1), ls->dentry_name); fwrite_buffer(ref_info, 1, len, buf); free(ref_info); @@ -1932,6 +1932,7 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls) free(ref_info); } } + free(ref); } static void update_remote_info_refs(struct remote_lock *lock) diff --git a/http-walker.c b/http-walker.c index 7bda34d914..99f397e32b 100644 --- a/http-walker.c +++ b/http-walker.c @@ -888,10 +888,10 @@ static int fetch(struct walker *walker, unsigned char *sha1) data->alt->base); } -static int fetch_ref(struct walker *walker, char *ref, unsigned char *sha1) +static int fetch_ref(struct walker *walker, struct ref *ref) { struct walker_data *data = walker->data; - return http_fetch_ref(data->alt->base, ref, sha1); + return http_fetch_ref(data->alt->base, ref); } static void cleanup(struct walker *walker) @@ -589,8 +589,9 @@ static char *quote_ref_url(const char *base, const char *ref) len += 2; /* extra two hex plus replacement % */ qref = xmalloc(len); memcpy(qref, base, baselen); - memcpy(qref + baselen, "/refs/", 6); - for (cp = ref, dp = qref + baselen + 6; (ch = *cp) != 0; cp++) { + dp = qref + baselen; + *(dp++) = '/'; + for (cp = ref; (ch = *cp) != 0; cp++) { if (needs_quote(ch)) { *dp++ = '%'; *dp++ = hex((ch >> 4) & 0xF); @@ -604,7 +605,7 @@ static char *quote_ref_url(const char *base, const char *ref) return qref; } -int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1) +int http_fetch_ref(const char *base, struct ref *ref) { char *url; struct strbuf buffer = STRBUF_INIT; @@ -612,7 +613,7 @@ int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1) struct slot_results results; int ret; - url = quote_ref_url(base, ref); + url = quote_ref_url(base, ref->name); slot = get_active_slot(); slot->results = &results; curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); @@ -624,12 +625,15 @@ int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1) if (results.curl_result == CURLE_OK) { strbuf_rtrim(&buffer); if (buffer.len == 40) - ret = get_sha1_hex(buffer.buf, sha1); - else + ret = get_sha1_hex(buffer.buf, ref->old_sha1); + else if (!prefixcmp(buffer.buf, "ref: ")) { + ref->symref = xstrdup(buffer.buf + 5); + ret = 0; + } else ret = 1; } else { ret = error("Couldn't get %s for %s\n%s", - url, ref, curl_errorstr); + url, ref->name, curl_errorstr); } } else { ret = error("Unable to start request"); @@ -105,6 +105,6 @@ static inline int missing__target(int code, int result) #define missing_target(a) missing__target((a)->http_code, (a)->curl_result) -extern int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1); +extern int http_fetch_ref(const char *base, struct ref *ref); #endif /* HTTP_H */ diff --git a/name-hash.c b/name-hash.c new file mode 100644 index 0000000000..0031d78e8c --- /dev/null +++ b/name-hash.c @@ -0,0 +1,119 @@ +/* + * name-hash.c + * + * Hashing names in the index state + * + * Copyright (C) 2008 Linus Torvalds + */ +#define NO_THE_INDEX_COMPATIBILITY_MACROS +#include "cache.h" + +/* + * This removes bit 5 if bit 6 is set. + * + * That will make US-ASCII characters hash to their upper-case + * equivalent. We could easily do this one whole word at a time, + * but that's for future worries. + */ +static inline unsigned char icase_hash(unsigned char c) +{ + return c & ~((c & 0x40) >> 1); +} + +static unsigned int hash_name(const char *name, int namelen) +{ + unsigned int hash = 0x123; + + do { + unsigned char c = *name++; + c = icase_hash(c); + hash = hash*101 + c; + } while (--namelen); + return hash; +} + +static void hash_index_entry(struct index_state *istate, struct cache_entry *ce) +{ + void **pos; + unsigned int hash; + + if (ce->ce_flags & CE_HASHED) + return; + ce->ce_flags |= CE_HASHED; + ce->next = NULL; + hash = hash_name(ce->name, ce_namelen(ce)); + pos = insert_hash(hash, ce, &istate->name_hash); + if (pos) { + ce->next = *pos; + *pos = ce; + } +} + +static void lazy_init_name_hash(struct index_state *istate) +{ + int nr; + + if (istate->name_hash_initialized) + return; + for (nr = 0; nr < istate->cache_nr; nr++) + hash_index_entry(istate, istate->cache[nr]); + istate->name_hash_initialized = 1; +} + +void add_name_hash(struct index_state *istate, struct cache_entry *ce) +{ + ce->ce_flags &= ~CE_UNHASHED; + if (istate->name_hash_initialized) + hash_index_entry(istate, ce); +} + +static int slow_same_name(const char *name1, int len1, const char *name2, int len2) +{ + if (len1 != len2) + return 0; + + while (len1) { + unsigned char c1 = *name1++; + unsigned char c2 = *name2++; + len1--; + if (c1 != c2) { + c1 = toupper(c1); + c2 = toupper(c2); + if (c1 != c2) + return 0; + } + } + return 1; +} + +static int same_name(const struct cache_entry *ce, const char *name, int namelen, int icase) +{ + int len = ce_namelen(ce); + + /* + * Always do exact compare, even if we want a case-ignoring comparison; + * we do the quick exact one first, because it will be the common case. + */ + if (len == namelen && !cache_name_compare(name, namelen, ce->name, len)) + return 1; + + return icase && slow_same_name(name, namelen, ce->name, len); +} + +struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase) +{ + unsigned int hash = hash_name(name, namelen); + struct cache_entry *ce; + + lazy_init_name_hash(istate); + ce = lookup_hash(hash, &istate->name_hash); + + while (ce) { + if (!(ce->ce_flags & CE_UNHASHED)) { + if (same_name(ce, name, namelen, icase)) + return ce; + } + ce = ce->next; + } + return NULL; +} diff --git a/read-cache.c b/read-cache.c index c3692f41ad..0382804e76 100644 --- a/read-cache.c +++ b/read-cache.c @@ -23,80 +23,21 @@ struct index_state the_index; -static unsigned int hash_name(const char *name, int namelen) -{ - unsigned int hash = 0x123; - - do { - unsigned char c = *name++; - hash = hash*101 + c; - } while (--namelen); - return hash; -} - -static void hash_index_entry(struct index_state *istate, struct cache_entry *ce) -{ - void **pos; - unsigned int hash; - - if (ce->ce_flags & CE_HASHED) - return; - ce->ce_flags |= CE_HASHED; - ce->next = NULL; - hash = hash_name(ce->name, ce_namelen(ce)); - pos = insert_hash(hash, ce, &istate->name_hash); - if (pos) { - ce->next = *pos; - *pos = ce; - } -} - -static void lazy_init_name_hash(struct index_state *istate) -{ - int nr; - - if (istate->name_hash_initialized) - return; - for (nr = 0; nr < istate->cache_nr; nr++) - hash_index_entry(istate, istate->cache[nr]); - istate->name_hash_initialized = 1; -} - static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce) { - ce->ce_flags &= ~CE_UNHASHED; istate->cache[nr] = ce; - if (istate->name_hash_initialized) - hash_index_entry(istate, ce); + add_name_hash(istate, ce); } static void replace_index_entry(struct index_state *istate, int nr, struct cache_entry *ce) { struct cache_entry *old = istate->cache[nr]; - remove_index_entry(old); + remove_name_hash(old); set_index_entry(istate, nr, ce); istate->cache_changed = 1; } -int index_name_exists(struct index_state *istate, const char *name, int namelen) -{ - unsigned int hash = hash_name(name, namelen); - struct cache_entry *ce; - - lazy_init_name_hash(istate); - ce = lookup_hash(hash, &istate->name_hash); - - while (ce) { - if (!(ce->ce_flags & CE_UNHASHED)) { - if (!cache_name_compare(name, namelen, ce->name, ce->ce_flags)) - return 1; - } - ce = ce->next; - } - return 0; -} - /* * This only updates the "non-critical" parts of the directory * cache, ie the parts that aren't tracked by GIT, and only used @@ -257,7 +198,8 @@ 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 (istate->timestamp && + return (!S_ISGITLINK(ce->ce_mode) && + istate->timestamp && ((unsigned int)istate->timestamp) <= ce->ce_mtime); } @@ -438,7 +380,7 @@ int remove_index_entry_at(struct index_state *istate, int pos) { struct cache_entry *ce = istate->cache[pos]; - remove_index_entry(ce); + remove_name_hash(ce); istate->cache_changed = 1; istate->cache_nr--; if (pos >= istate->cache_nr) @@ -488,21 +430,50 @@ static int index_name_pos_also_unmerged(struct index_state *istate, return pos; } -int add_file_to_index(struct index_state *istate, const char *path, int verbose) +static int different_name(struct cache_entry *ce, struct cache_entry *alias) { - int size, namelen, pos; - struct stat st; - struct cache_entry *ce; - unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY; + int len = ce_namelen(ce); + return ce_namelen(alias) != len || memcmp(ce->name, alias->name, len); +} - if (lstat(path, &st)) - die("%s: unable to stat (%s)", path, strerror(errno)); +/* + * If we add a filename that aliases in the cache, we will use the + * name that we already have - but we don't want to update the same + * alias twice, because that implies that there were actually two + * different files with aliasing names! + * + * So we use the CE_ADDED flag to verify that the alias was an old + * one before we accept it as + */ +static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_entry *alias) +{ + int len; + struct cache_entry *new; + + if (alias->ce_flags & CE_ADDED) + die("Will not add file alias '%s' ('%s' already exists in index)", ce->name, alias->name); + + /* Ok, create the new entry using the name of the existing alias */ + len = ce_namelen(alias); + new = xcalloc(1, cache_entry_size(len)); + memcpy(new->name, alias->name, len); + copy_cache_entry(new, ce); + free(ce); + return new; +} + +int add_to_index(struct index_state *istate, const char *path, struct stat *st, int verbose) +{ + int size, namelen; + mode_t st_mode = st->st_mode; + struct cache_entry *ce, *alias; + unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY; - if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode)) + if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode)) die("%s: can only add regular files, symbolic links or git-directories", path); namelen = strlen(path); - if (S_ISDIR(st.st_mode)) { + if (S_ISDIR(st_mode)) { while (namelen && path[namelen-1] == '/') namelen--; } @@ -510,10 +481,10 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose) ce = xcalloc(1, size); memcpy(ce->name, path, namelen); ce->ce_flags = namelen; - fill_stat_cache_info(ce, &st); + fill_stat_cache_info(ce, st); if (trust_executable_bit && has_symlinks) - ce->ce_mode = create_ce_mode(st.st_mode); + ce->ce_mode = create_ce_mode(st_mode); else { /* If there is an existing entry, pick the mode bits and type * from it, otherwise assume unexecutable regular file. @@ -522,21 +493,22 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose) int pos = index_name_pos_also_unmerged(istate, path, namelen); ent = (0 <= pos) ? istate->cache[pos] : NULL; - ce->ce_mode = ce_mode_from_stat(ent, st.st_mode); + ce->ce_mode = ce_mode_from_stat(ent, st_mode); } - pos = index_name_pos(istate, ce->name, namelen); - if (0 <= pos && - !ce_stage(istate->cache[pos]) && - !ie_match_stat(istate, istate->cache[pos], &st, ce_option)) { + alias = index_name_exists(istate, ce->name, ce_namelen(ce), ignore_case); + if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) { /* Nothing changed, really */ free(ce); - ce_mark_uptodate(istate->cache[pos]); + ce_mark_uptodate(alias); + alias->ce_flags |= CE_ADDED; return 0; } - - if (index_path(ce->sha1, path, &st, 1)) + if (index_path(ce->sha1, path, st, 1)) die("unable to index file %s", path); + if (ignore_case && alias && different_name(ce, alias)) + ce = create_alias_ce(ce, alias); + ce->ce_flags |= CE_ADDED; if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE)) die("unable to add %s to index",path); if (verbose) @@ -544,6 +516,14 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose) return 0; } +int add_file_to_index(struct index_state *istate, const char *path, int verbose) +{ + struct stat st; + if (lstat(path, &st)) + die("%s: unable to stat (%s)", path, strerror(errno)); + return add_to_index(istate, path, &st, verbose); +} + struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh) @@ -691,6 +691,13 @@ struct ref *alloc_ref(unsigned namelen) return ret; } +struct ref *alloc_ref_from_str(const char* str) +{ + struct ref *ret = alloc_ref(strlen(str) + 1); + strcpy(ret->name, str); + return ret; +} + static struct ref *copy_ref(const struct ref *ref) { struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1); @@ -711,13 +718,22 @@ struct ref *copy_ref_list(const struct ref *ref) return ret; } +void free_ref(struct ref *ref) +{ + if (!ref) + return; + free(ref->remote_status); + free(ref->symref); + free(ref); +} + void free_refs(struct ref *ref) { struct ref *next; while (ref) { next = ref->next; free(ref->peer_ref); - free(ref); + free_ref(ref); ref = next; } } @@ -788,7 +804,6 @@ static struct ref *try_explicit_object_name(const char *name) { unsigned char sha1[20]; struct ref *ref; - int len; if (!*name) { ref = alloc_ref(20); @@ -798,21 +813,14 @@ static struct ref *try_explicit_object_name(const char *name) } if (get_sha1(name, sha1)) return NULL; - len = strlen(name) + 1; - ref = alloc_ref(len); - memcpy(ref->name, name, len); + ref = alloc_ref_from_str(name); hashcpy(ref->new_sha1, sha1); return ref; } static struct ref *make_linked_ref(const char *name, struct ref ***tail) { - struct ref *ret; - size_t len; - - len = strlen(name) + 1; - ret = alloc_ref(len); - memcpy(ret->name, name, len); + struct ref *ret = alloc_ref_from_str(name); tail_link_ref(ret, tail); return ret; } @@ -1116,9 +1124,7 @@ static struct ref *get_local_ref(const char *name) return NULL; if (!prefixcmp(name, "refs/")) { - ret = alloc_ref(strlen(name) + 1); - strcpy(ret->name, name); - return ret; + return alloc_ref_from_str(name); } if (!prefixcmp(name, "heads/") || @@ -1177,3 +1183,15 @@ int get_fetch_map(const struct ref *remote_refs, return 0; } + +int resolve_remote_symref(struct ref *ref, struct ref *list) +{ + if (!ref->symref) + return 0; + for (; list; list = list->next) + if (!strcmp(ref->symref, list->name)) { + hashcpy(ref->old_sha1, list->old_sha1); + return 0; + } + return 1; +} @@ -54,6 +54,8 @@ struct refspec { struct ref *alloc_ref(unsigned namelen); +struct ref *alloc_ref_from_str(const char* str); + struct ref *copy_ref_list(const struct ref *ref); int check_ref_type(const struct ref *ref, int flags); @@ -63,6 +65,8 @@ 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); + /* * Removes and frees any duplicate refs in the map. */ diff --git a/symlinks.c b/symlinks.c index be9ace6c04..5a5e781a15 100644 --- a/symlinks.c +++ b/symlinks.c @@ -1,48 +1,64 @@ #include "cache.h" -int has_symlink_leading_path(const char *name, char *last_symlink) -{ +struct pathname { + int len; char path[PATH_MAX]; - const char *sp, *ep; - char *dp; - - sp = name; - dp = path; - - if (last_symlink && *last_symlink) { - size_t last_len = strlen(last_symlink); - size_t len = strlen(name); - if (last_len < len && - !strncmp(name, last_symlink, last_len) && - name[last_len] == '/') - return 1; - *last_symlink = '\0'; +}; + +/* Return matching pathname prefix length, or zero if not matching */ +static inline int match_pathname(int len, const char *name, struct pathname *match) +{ + int match_len = match->len; + return (len > match_len && + name[match_len] == '/' && + !memcmp(name, match->path, match_len)) ? match_len : 0; +} + +static inline void set_pathname(int len, const char *name, struct pathname *match) +{ + if (len < PATH_MAX) { + match->len = len; + memcpy(match->path, name, len); + match->path[len] = 0; } +} + +int has_symlink_leading_path(int len, const char *name) +{ + static struct pathname link, nonlink; + char path[PATH_MAX]; + struct stat st; + char *sp; + int known_dir; - while (1) { - size_t len; - struct stat st; + /* + * See if the last known symlink cache matches. + */ + if (match_pathname(len, name, &link)) + return 1; - ep = strchr(sp, '/'); - if (!ep) - break; - len = ep - sp; - if (PATH_MAX <= dp + len - path + 2) - return 0; /* new name is longer than that??? */ - memcpy(dp, sp, len); - dp[len] = 0; + /* + * Get rid of the last known directory part + */ + known_dir = match_pathname(len, name, &nonlink); + + while ((sp = strchr(name + known_dir + 1, '/')) != NULL) { + int thislen = sp - name ; + memcpy(path, name, thislen); + path[thislen] = 0; if (lstat(path, &st)) return 0; + if (S_ISDIR(st.st_mode)) { + set_pathname(thislen, path, &nonlink); + known_dir = thislen; + continue; + } if (S_ISLNK(st.st_mode)) { - if (last_symlink) - strcpy(last_symlink, path); + set_pathname(thislen, path, &link); return 1; } - - dp[len++] = '/'; - dp = dp + len; - sp = ep + 1; + break; } return 0; } diff --git a/t/.gitignore b/t/.gitignore index fad67c097b..11ffd910c1 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -1 +1 @@ -trash +/trash directory diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh index 9decd2e1e8..5d3bd9dda9 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -20,12 +20,13 @@ then fi svnrepo=$PWD/svnrepo +export svnrepo perl -w -e " use SVN::Core; use SVN::Repos; \$SVN::Core::VERSION gt '1.1.0' or exit(42); -system(qw/svnadmin create --fs-type fsfs/, '$svnrepo') == 0 or exit(41); +system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41); " >&3 2>&4 x=$? if test $x -ne 0 @@ -73,11 +74,16 @@ for d in \ done start_httpd () { + repo_base_path="$1" if test -z "$SVN_HTTPD_PORT" then echo >&2 'SVN_HTTPD_PORT is not defined!' return fi + if test -z "$repo_base_path" + then + repo_base_path=svn + fi mkdir "$GIT_DIR"/logs @@ -90,13 +96,13 @@ LockFile logs/accept.lock Listen 127.0.0.1:$SVN_HTTPD_PORT LoadModule dav_module $SVN_HTTPD_MODULE_PATH/mod_dav.so LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so -<Location /svn> +<Location /$repo_base_path> DAV svn SVNPath $rawsvnrepo </Location> EOF "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start - svnrepo=http://127.0.0.1:$SVN_HTTPD_PORT/svn + svnrepo="http://127.0.0.1:$SVN_HTTPD_PORT/$repo_base_path" } stop_httpd () { diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index 7f206c56cf..a5c4436fd1 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -61,7 +61,8 @@ prepare_httpd() { -new -x509 -nodes \ -out $HTTPD_ROOT_PATH/httpd.pem \ -keyout $HTTPD_ROOT_PATH/httpd.pem - export GIT_SSL_NO_VERIFY=t + GIT_SSL_NO_VERIFY=t + export GIT_SSL_NO_VERIFY HTTPD_PARA="$HTTPD_PARA -DSSL" else HTTPD_URL=http://127.0.0.1:$LIB_HTTPD_PORT diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 27b54cbb12..690f80ab27 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -305,10 +305,10 @@ test_expect_success 'absolute path works as expected' ' file="$dir"/index && test "$file" = "$(test-absolute-path $dir2/index)" && basename=blub && - test "$dir/$basename" = $(cd .git && test-absolute-path $basename) && + test "$dir/$basename" = "$(cd .git && test-absolute-path "$basename")" && ln -s ../first/file .git/syml && sym="$(cd first; pwd -P)"/file && - test "$sym" = "$(test-absolute-path $dir2/syml)" + test "$sym" = "$(test-absolute-path "$dir2/syml")" ' test_expect_success 'very long name in the index handled sanely' ' diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh index b9cef3422c..fc386ba033 100755 --- a/t/t1020-subdirectory.sh +++ b/t/t1020-subdirectory.sh @@ -21,7 +21,7 @@ LF=' ' test_expect_success 'update-index and ls-files' ' - cd $HERE && + cd "$HERE" && git update-index --add one && case "`git ls-files`" in one) echo ok one ;; @@ -41,7 +41,7 @@ test_expect_success 'update-index and ls-files' ' ' test_expect_success 'cat-file' ' - cd $HERE && + cd "$HERE" && two=`git ls-files -s dir/two` && two=`expr "$two" : "[0-7]* \\([0-9a-f]*\\)"` && echo "$two" && @@ -54,7 +54,7 @@ test_expect_success 'cat-file' ' rm -f actual dir/actual test_expect_success 'diff-files' ' - cd $HERE && + cd "$HERE" && echo a >>one && echo d >>dir/two && case "`git diff-files --name-only`" in @@ -74,7 +74,7 @@ test_expect_success 'diff-files' ' ' test_expect_success 'write-tree' ' - cd $HERE && + cd "$HERE" && top=`git write-tree` && echo $top && cd dir && @@ -84,7 +84,7 @@ test_expect_success 'write-tree' ' ' test_expect_success 'checkout-index' ' - cd $HERE && + cd "$HERE" && git checkout-index -f -u one && cmp one original.one && cd dir && @@ -93,7 +93,7 @@ test_expect_success 'checkout-index' ' ' test_expect_success 'read-tree' ' - cd $HERE && + cd "$HERE" && rm -f one dir/two && tree=`git write-tree` && git read-tree --reset -u "$tree" && @@ -107,27 +107,27 @@ test_expect_success 'read-tree' ' ' test_expect_success 'no file/rev ambiguity check inside .git' ' - cd $HERE && + cd "$HERE" && git commit -a -m 1 && - cd $HERE/.git && + cd "$HERE"/.git && git show -s HEAD ' test_expect_success 'no file/rev ambiguity check inside a bare repo' ' - cd $HERE && + cd "$HERE" && git clone -s --bare .git foo.git && cd foo.git && GIT_DIR=. git show -s HEAD ' # This still does not work as it should... : test_expect_success 'no file/rev ambiguity check inside a bare repo' ' - cd $HERE && + cd "$HERE" && git clone -s --bare .git foo.git && cd foo.git && git show -s HEAD ' test_expect_success 'detection should not be fooled by a symlink' ' - cd $HERE && + cd "$HERE" && rm -fr foo.git && git clone -s .git another && ln -s another yetanother && diff --git a/t/t1303-wacky-config.sh b/t/t1303-wacky-config.sh index 99985dcd79..f366b53fb6 100755 --- a/t/t1303-wacky-config.sh +++ b/t/t1303-wacky-config.sh @@ -34,4 +34,10 @@ test_expect_success 'add key in different section' ' check section2.key bar ' +SECTION="test.q\"s\\sq'sp e.key" +test_expect_success 'make sure git-config escapes section names properly' ' + git config "$SECTION" bar && + check "$SECTION" bar +' + test_done diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh index 38a2bf09af..85da4caa7e 100755 --- a/t/t1500-rev-parse.sh +++ b/t/t1500-rev-parse.sh @@ -51,8 +51,9 @@ test_rev_parse 'core.bare undefined' false false true mkdir work || exit 1 cd work || exit 1 -export GIT_DIR=../.git -export GIT_CONFIG="$(pwd)"/../.git/config +GIT_DIR=../.git +GIT_CONFIG="$(pwd)"/../.git/config +export GIT_DIR GIT_CONFIG git config core.bare false test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true '' @@ -64,8 +65,8 @@ git config --unset core.bare test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true '' mv ../.git ../repo.git || exit 1 -export GIT_DIR=../repo.git -export GIT_CONFIG="$(pwd)"/../repo.git/config +GIT_DIR=../repo.git +GIT_CONFIG="$(pwd)"/../repo.git/config git config core.bare false test_rev_parse 'GIT_DIR=../repo.git, core.bare = false' false false true '' diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh index 7ee3820ce9..2ee88d8a06 100755 --- a/t/t1501-worktree.sh +++ b/t/t1501-worktree.sh @@ -32,24 +32,25 @@ mkdir -p work/sub/dir || exit 1 mv .git repo.git || exit 1 say "core.worktree = relative path" -export GIT_DIR=repo.git -export GIT_CONFIG="$(pwd)"/$GIT_DIR/config +GIT_DIR=repo.git +GIT_CONFIG="$(pwd)"/$GIT_DIR/config +export GIT_DIR GIT_CONFIG unset GIT_WORK_TREE git config core.worktree ../work test_rev_parse 'outside' false false false cd work || exit 1 -export GIT_DIR=../repo.git -export GIT_CONFIG="$(pwd)"/$GIT_DIR/config +GIT_DIR=../repo.git +GIT_CONFIG="$(pwd)"/$GIT_DIR/config test_rev_parse 'inside' false false true '' cd sub/dir || exit 1 -export GIT_DIR=../../../repo.git -export GIT_CONFIG="$(pwd)"/$GIT_DIR/config +GIT_DIR=../../../repo.git +GIT_CONFIG="$(pwd)"/$GIT_DIR/config test_rev_parse 'subdirectory' false false true sub/dir/ cd ../../.. || exit 1 say "core.worktree = absolute path" -export GIT_DIR=$(pwd)/repo.git -export GIT_CONFIG=$GIT_DIR/config +GIT_DIR=$(pwd)/repo.git +GIT_CONFIG=$GIT_DIR/config git config core.worktree "$(pwd)/work" test_rev_parse 'outside' false false false cd work || exit 1 @@ -59,25 +60,26 @@ test_rev_parse 'subdirectory' false false true sub/dir/ cd ../../.. || exit 1 say "GIT_WORK_TREE=relative path (override core.worktree)" -export GIT_DIR=$(pwd)/repo.git -export GIT_CONFIG=$GIT_DIR/config +GIT_DIR=$(pwd)/repo.git +GIT_CONFIG=$GIT_DIR/config git config core.worktree non-existent -export GIT_WORK_TREE=work +GIT_WORK_TREE=work +export GIT_WORK_TREE test_rev_parse 'outside' false false false cd work || exit 1 -export GIT_WORK_TREE=. +GIT_WORK_TREE=. test_rev_parse 'inside' false false true '' cd sub/dir || exit 1 -export GIT_WORK_TREE=../.. +GIT_WORK_TREE=../.. test_rev_parse 'subdirectory' false false true sub/dir/ cd ../../.. || exit 1 mv work repo.git/work say "GIT_WORK_TREE=absolute path, work tree below git dir" -export GIT_DIR=$(pwd)/repo.git -export GIT_CONFIG=$GIT_DIR/config -export GIT_WORK_TREE=$(pwd)/repo.git/work +GIT_DIR=$(pwd)/repo.git +GIT_CONFIG=$GIT_DIR/config +GIT_WORK_TREE=$(pwd)/repo.git/work test_rev_parse 'outside' false false false cd repo.git || exit 1 test_rev_parse 'in repo.git' false true false diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh new file mode 100755 index 0000000000..95244c9bcf --- /dev/null +++ b/t/t1503-rev-parse-verify.sh @@ -0,0 +1,107 @@ +#!/bin/sh +# +# Copyright (c) 2008 Christian Couder +# +test_description='test git rev-parse --verify' + +exec </dev/null + +. ./test-lib.sh + +add_line_into_file() +{ + _line=$1 + _file=$2 + + if [ -f "$_file" ]; then + echo "$_line" >> $_file || return $? + MSG="Add <$_line> into <$_file>." + else + echo "$_line" > $_file || return $? + git add $_file || return $? + MSG="Create file <$_file> with <$_line> inside." + fi + + test_tick + git-commit --quiet -m "$MSG" $_file +} + +HASH1= +HASH2= +HASH3= +HASH4= + +test_expect_success 'set up basic repo with 1 file (hello) and 4 commits' ' + add_line_into_file "1: Hello World" hello && + HASH1=$(git rev-parse --verify HEAD) && + add_line_into_file "2: A new day for git" hello && + HASH2=$(git rev-parse --verify HEAD) && + add_line_into_file "3: Another new day for git" hello && + HASH3=$(git rev-parse --verify HEAD) && + add_line_into_file "4: Ciao for now" hello && + HASH4=$(git rev-parse --verify HEAD) +' + +test_expect_success 'works with one good rev' ' + rev_hash1=$(git rev-parse --verify $HASH1) && + test "$rev_hash1" = "$HASH1" && + rev_hash2=$(git rev-parse --verify $HASH2) && + test "$rev_hash2" = "$HASH2" && + rev_hash3=$(git rev-parse --verify $HASH3) && + test "$rev_hash3" = "$HASH3" && + rev_hash4=$(git rev-parse --verify $HASH4) && + test "$rev_hash4" = "$HASH4" && + rev_master=$(git rev-parse --verify master) && + test "$rev_master" = "$HASH4" && + rev_head=$(git rev-parse --verify HEAD) && + test "$rev_head" = "$HASH4" +' + +test_expect_success 'fails with any bad rev or many good revs' ' + test_must_fail git rev-parse --verify 2>error && + grep "single revision" error && + test_must_fail git rev-parse --verify foo 2>error && + grep "single revision" error && + test_must_fail git rev-parse --verify HEAD bar 2>error && + grep "single revision" error && + test_must_fail git rev-parse --verify baz HEAD 2>error && + grep "single revision" error && + test_must_fail git rev-parse --verify $HASH2 HEAD 2>error && + grep "single revision" error +' + +test_expect_success 'fails silently when using -q' ' + test_must_fail git rev-parse --verify --quiet 2>error && + test -z "$(cat error)" && + test_must_fail git rev-parse -q --verify foo 2>error && + test -z "$(cat error)" && + test_must_fail git rev-parse --verify -q HEAD bar 2>error && + test -z "$(cat error)" && + test_must_fail git rev-parse --quiet --verify baz HEAD 2>error && + test -z "$(cat error)" && + test_must_fail git rev-parse -q --verify $HASH2 HEAD 2>error && + test -z "$(cat error)" +' + +test_expect_success 'no stdout output on error' ' + test -z "$(git rev-parse --verify)" && + test -z "$(git rev-parse --verify foo)" && + test -z "$(git rev-parse --verify baz HEAD)" && + test -z "$(git rev-parse --verify HEAD bar)" && + test -z "$(git rev-parse --verify $HASH2 HEAD)" +' + +test_expect_success 'use --default' ' + git rev-parse --verify --default master && + git rev-parse --verify --default master HEAD && + git rev-parse --default master --verify && + git rev-parse --default master --verify HEAD && + git rev-parse --verify HEAD --default master && + test_must_fail git rev-parse --verify foo --default master && + test_must_fail git rev-parse --default HEAD --verify bar && + test_must_fail git rev-parse --verify --default HEAD baz && + test_must_fail git rev-parse --default foo --verify && + test_must_fail git rev-parse --verify --default bar +' + +test_done diff --git a/t/t3050-subprojects-fetch.sh b/t/t3050-subprojects-fetch.sh index 2b21b1070d..4261e9641e 100755 --- a/t/t3050-subprojects-fetch.sh +++ b/t/t3050-subprojects-fetch.sh @@ -20,7 +20,7 @@ test_expect_success setup ' ' test_expect_success clone ' - git clone file://`pwd`/.git cloned && + git clone "file://$(pwd)/.git" cloned && (git rev-parse HEAD; git ls-files -s) >expected && ( cd cloned && diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index cb5f7a4441..8d8768688d 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -224,4 +224,238 @@ test_expect_success 'avoid ambiguous track' ' test -z "$(git config branch.all1.merge)" ' +test_expect_success 'autosetuprebase local on a tracked local branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase local && + (git show-ref -q refs/remotes/local/o || git-fetch local) && + git branch mybase && + git branch --track myr1 mybase && + test "$(git config branch.myr1.remote)" = . && + test "$(git config branch.myr1.merge)" = refs/heads/mybase && + test "$(git config branch.myr1.rebase)" = true +' + +test_expect_success 'autosetuprebase always on a tracked local branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase always && + (git show-ref -q refs/remotes/local/o || git-fetch local) && + git branch mybase2 && + git branch --track myr2 mybase && + test "$(git config branch.myr2.remote)" = . && + test "$(git config branch.myr2.merge)" = refs/heads/mybase && + test "$(git config branch.myr2.rebase)" = true +' + +test_expect_success 'autosetuprebase remote on a tracked local branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase remote && + (git show-ref -q refs/remotes/local/o || git-fetch local) && + git branch mybase3 && + git branch --track myr3 mybase2 && + test "$(git config branch.myr3.remote)" = . && + test "$(git config branch.myr3.merge)" = refs/heads/mybase2 && + ! test "$(git config branch.myr3.rebase)" = true +' + +test_expect_success 'autosetuprebase never on a tracked local branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase never && + (git show-ref -q refs/remotes/local/o || git-fetch local) && + git branch mybase4 && + git branch --track myr4 mybase2 && + test "$(git config branch.myr4.remote)" = . && + test "$(git config branch.myr4.merge)" = refs/heads/mybase2 && + ! test "$(git config branch.myr4.rebase)" = true +' + +test_expect_success 'autosetuprebase local on a tracked remote branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase local && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --track myr5 local/master && + test "$(git config branch.myr5.remote)" = local && + test "$(git config branch.myr5.merge)" = refs/heads/master && + ! test "$(git config branch.myr5.rebase)" = true +' + +test_expect_success 'autosetuprebase never on a tracked remote branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase never && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --track myr6 local/master && + test "$(git config branch.myr6.remote)" = local && + test "$(git config branch.myr6.merge)" = refs/heads/master && + ! test "$(git config branch.myr6.rebase)" = true +' + +test_expect_success 'autosetuprebase remote on a tracked remote branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase remote && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --track myr7 local/master && + test "$(git config branch.myr7.remote)" = local && + test "$(git config branch.myr7.merge)" = refs/heads/master && + test "$(git config branch.myr7.rebase)" = true +' + +test_expect_success 'autosetuprebase always on a tracked remote branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + git config branch.autosetuprebase remote && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --track myr8 local/master && + test "$(git config branch.myr8.remote)" = local && + test "$(git config branch.myr8.merge)" = refs/heads/master && + test "$(git config branch.myr8.rebase)" = true +' + +test_expect_success 'autosetuprebase unconfigured on a tracked remote branch' ' + git config --unset branch.autosetuprebase && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --track myr9 local/master && + test "$(git config branch.myr9.remote)" = local && + test "$(git config branch.myr9.merge)" = refs/heads/master && + test "z$(git config branch.myr9.rebase)" = z +' + +test_expect_success 'autosetuprebase unconfigured on a tracked local branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/o || git-fetch local) && + git branch mybase10 && + git branch --track myr10 mybase2 && + test "$(git config branch.myr10.remote)" = . && + test "$(git config branch.myr10.merge)" = refs/heads/mybase2 && + test "z$(git config branch.myr10.rebase)" = z +' + +test_expect_success 'autosetuprebase unconfigured on untracked local branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr11 mybase2 && + test "z$(git config branch.myr11.remote)" = z && + test "z$(git config branch.myr11.merge)" = z && + test "z$(git config branch.myr11.rebase)" = z +' + +test_expect_success 'autosetuprebase unconfigured on untracked remote branch' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr12 local/master && + test "z$(git config branch.myr12.remote)" = z && + test "z$(git config branch.myr12.merge)" = z && + test "z$(git config branch.myr12.rebase)" = z +' + +test_expect_success 'autosetuprebase never on an untracked local branch' ' + git config branch.autosetuprebase never && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr13 mybase2 && + test "z$(git config branch.myr13.remote)" = z && + test "z$(git config branch.myr13.merge)" = z && + test "z$(git config branch.myr13.rebase)" = z +' + +test_expect_success 'autosetuprebase local on an untracked local branch' ' + git config branch.autosetuprebase local && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr14 mybase2 && + test "z$(git config branch.myr14.remote)" = z && + test "z$(git config branch.myr14.merge)" = z && + test "z$(git config branch.myr14.rebase)" = z +' + +test_expect_success 'autosetuprebase remote on an untracked local branch' ' + git config branch.autosetuprebase remote && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr15 mybase2 && + test "z$(git config branch.myr15.remote)" = z && + test "z$(git config branch.myr15.merge)" = z && + test "z$(git config branch.myr15.rebase)" = z +' + +test_expect_success 'autosetuprebase always on an untracked local branch' ' + git config branch.autosetuprebase always && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr16 mybase2 && + test "z$(git config branch.myr16.remote)" = z && + test "z$(git config branch.myr16.merge)" = z && + test "z$(git config branch.myr16.rebase)" = z +' + +test_expect_success 'autosetuprebase never on an untracked remote branch' ' + git config branch.autosetuprebase never && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr17 local/master && + test "z$(git config branch.myr17.remote)" = z && + test "z$(git config branch.myr17.merge)" = z && + test "z$(git config branch.myr17.rebase)" = z +' + +test_expect_success 'autosetuprebase local on an untracked remote branch' ' + git config branch.autosetuprebase local && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr18 local/master && + test "z$(git config branch.myr18.remote)" = z && + test "z$(git config branch.myr18.merge)" = z && + test "z$(git config branch.myr18.rebase)" = z +' + +test_expect_success 'autosetuprebase remote on an untracked remote branch' ' + git config branch.autosetuprebase remote && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr19 local/master && + test "z$(git config branch.myr19.remote)" = z && + test "z$(git config branch.myr19.merge)" = z && + test "z$(git config branch.myr19.rebase)" = z +' + +test_expect_success 'autosetuprebase always on an untracked remote branch' ' + git config branch.autosetuprebase always && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git-fetch local) && + git branch --no-track myr20 local/master && + test "z$(git config branch.myr20.remote)" = z && + test "z$(git config branch.myr20.merge)" = z && + test "z$(git config branch.myr20.rebase)" = z +' + +test_expect_success 'detect misconfigured autosetuprebase (bad value)' ' + git config branch.autosetuprebase garbage && + test_must_fail git branch +' + +test_expect_success 'detect misconfigured autosetuprebase (no value)' ' + git config --unset branch.autosetuprebase && + echo "[branch] autosetuprebase" >> .git/config && + test_must_fail git branch && + git config --unset branch.autosetuprebase +' + test_done diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index 496f4ec172..91bb5e1d9e 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -9,7 +9,8 @@ This test runs git rebase and checks that the author information is not lost. ' . ./test-lib.sh -export GIT_AUTHOR_EMAIL=bogus_email_address +GIT_AUTHOR_EMAIL=bogus_email_address +export GIT_AUTHOR_EMAIL test_expect_success \ 'prepare repository with topic branches' \ @@ -44,13 +45,13 @@ test_expect_success 'rebase against master' ' test_expect_success \ 'the rebase operation should not have destroyed author information' \ - '! git log | grep "Author:" | grep "<>"' + '! (git log | grep "Author:" | grep "<>")' test_expect_success 'rebase after merge master' ' git reset --hard topic && git merge master && git rebase master && - ! git show | grep "^Merge:" + ! (git show | grep "^Merge:") ' test_expect_success 'rebase of history with merges is linearized' ' diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 9cf873f7eb..b9e3dbd242 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -91,9 +91,8 @@ for line in $FAKE_LINES; do done EOF +test_set_editor "$(pwd)/fake-editor.sh" chmod a+x fake-editor.sh -VISUAL="$(pwd)/fake-editor.sh" -export VISUAL test_expect_success 'no changes are a nop' ' git rebase -i F && diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh index 37944c39a3..1777ffe8a2 100755 --- a/t/t3407-rebase-abort.sh +++ b/t/t3407-rebase-abort.sh @@ -4,7 +4,13 @@ test_description='git rebase --abort tests' . ./test-lib.sh +### Test that we handle space characters properly +work_dir="$(pwd)/test dir" + test_expect_success setup ' + mkdir -p "$work_dir" && + cd "$work_dir" && + git init && echo a > a && git add a && git commit -m a && @@ -28,32 +34,35 @@ testrebase() { dotest=$2 test_expect_success "rebase$type --abort" ' + cd "$work_dir" && # Clean up the state from the previous one - git reset --hard pre-rebase - test_must_fail git rebase'"$type"' master && - test -d '$dotest' && + git reset --hard pre-rebase && + test_must_fail git rebase$type master && + test -d "$dotest" && git rebase --abort && test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) && - test ! -d '$dotest' + test ! -d "$dotest" ' test_expect_success "rebase$type --abort after --skip" ' + cd "$work_dir" && # Clean up the state from the previous one - git reset --hard pre-rebase - test_must_fail git rebase'"$type"' master && - test -d '$dotest' && + git reset --hard pre-rebase && + test_must_fail git rebase$type master && + test -d "$dotest" && test_must_fail git rebase --skip && test $(git rev-parse HEAD) = $(git rev-parse master) && git-rebase --abort && test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) && - test ! -d '$dotest' + test ! -d "$dotest" ' test_expect_success "rebase$type --abort after --continue" ' + cd "$work_dir" && # Clean up the state from the previous one - git reset --hard pre-rebase - test_must_fail git rebase'"$type"' master && - test -d '$dotest' && + git reset --hard pre-rebase && + test_must_fail git rebase$type master && + test -d "$dotest" && echo c > a && echo d >> a && git add a && @@ -61,7 +70,7 @@ testrebase() { test $(git rev-parse HEAD) != $(git rev-parse master) && git rebase --abort && test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) && - test ! -d '$dotest' + test ! -d "$dotest" ' } diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh index d0a440feba..4911c48378 100755 --- a/t/t3500-cherry.sh +++ b/t/t3500-cherry.sh @@ -10,7 +10,8 @@ checks that git cherry only returns the second patch in the local branch ' . ./test-lib.sh -export GIT_AUTHOR_EMAIL=bogus_email_address +GIT_AUTHOR_EMAIL=bogus_email_address +export GIT_AUTHOR_EMAIL test_expect_success \ 'prepare repository with topic branch, and check cherry finds the 2 patches from there' \ diff --git a/t/t3700-add.sh b/t/t3700-add.sh index 287e058e37..68c5ddebdf 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -81,17 +81,17 @@ test_expect_success '.gitignore test setup' ' test_expect_success '.gitignore is honored' ' git add . && - ! git ls-files | grep "\\.ig" + ! (git ls-files | grep "\\.ig") ' test_expect_success 'error out when attempting to add ignored ones without -f' ' ! git add a.?? && - ! git ls-files | grep "\\.ig" + ! (git ls-files | grep "\\.ig") ' test_expect_success 'error out when attempting to add ignored ones without -f' ' ! git add d.?? && - ! git ls-files | grep "\\.ig" + ! (git ls-files | grep "\\.ig") ' test_expect_success 'add ignored ones with -f' ' diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh index 1fd3fb74d7..ba6679c6e4 100755 --- a/t/t4027-diff-submodule.sh +++ b/t/t4027-diff-submodule.sh @@ -50,4 +50,11 @@ test_expect_success 'git diff-files --raw' ' test_cmp expect actual.files ' +test_expect_success 'git diff (empty submodule dir)' ' + : >empty && + rm -rf sub/* sub/.git && + git diff > actual.empty && + test_cmp empty actual.empty +' + test_done diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index fa62b6aa21..9b0baac8db 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -67,10 +67,10 @@ test_expect_success \ test_expect_success \ 'validate file modification time' \ - 'TZ=GMT $TAR tvf b.tar a/a | - awk \{print\ \$4,\ \(length\(\$5\)\<7\)\ ?\ \$5\":00\"\ :\ \$5\} \ - >b.mtime && - echo "2005-05-27 22:00:00" >expected.mtime && + 'mkdir extract && + $TAR xf b.tar -C extract a/a && + perl -e '\''print((stat("extract/a/a"))[9], "\n")'\'' >b.mtime && + echo "1117231200" >expected.mtime && diff expected.mtime b.mtime' test_expect_success \ diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index b88b5bbd02..09fd917672 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh @@ -65,7 +65,7 @@ test_expect_success \ have_64bits= if msg=$(git verify-pack -v "test-3-${pack3}.pack" 2>&1) || - ! echo "$msg" | grep "pack too large .* off_t" + ! (echo "$msg" | grep "pack too large .* off_t") then have_64bits=t else diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 788b4a5aae..140e874581 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -31,7 +31,7 @@ add () { sec=$(($sec+1)) commit=$(echo "$text" | GIT_AUTHOR_DATE=$sec \ git commit-tree $tree $parents 2>>log2.txt) - export $name=$commit + eval "$name=$commit; export $name" echo $commit > .git/refs/heads/$branch eval ${branch}TIP=$commit } @@ -129,7 +129,7 @@ pull_to_client 2nd "B" $((64*3)) pull_to_client 3rd "A" $((1*3)) # old fails -test_expect_success "clone shallow" "git-clone --depth 2 file://`pwd`/. shallow" +test_expect_success "clone shallow" 'git-clone --depth 2 "file://$(pwd)/." shallow' (cd shallow; git count-objects -v) > count.shallow diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh index c0dc94909b..1dd8eed5bb 100755 --- a/t/t5512-ls-remote.sh +++ b/t/t5512-ls-remote.sh @@ -17,7 +17,7 @@ test_expect_success setup ' git show-ref -d | sed -e "s/ / /" ) >expected.all && - git remote add self $(pwd)/.git + git remote add self "$(pwd)/.git" ' diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 0a757d5b9f..3af03d4827 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -105,7 +105,7 @@ test_expect_success 'fetch with insteadOf' ' ( TRASH=$(pwd)/ && cd testrepo && - git config url.$TRASH.insteadOf trash/ + git config "url.$TRASH.insteadOf" trash/ && git config remote.up.url trash/. && git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" && git fetch up && @@ -145,8 +145,8 @@ test_expect_success 'push with wildcard' ' test_expect_success 'push with insteadOf' ' mk_empty && - TRASH=$(pwd)/ && - git config url.$TRASH.insteadOf trash/ && + TRASH="$(pwd)/" && + git config "url./$TRASH/.insteadOf" trash/ && git push trash/testrepo refs/heads/master:refs/remotes/origin/master && ( cd testrepo && diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh index b6a54867b4..e5619a9f5c 100755 --- a/t/t5700-clone-reference.sh +++ b/t/t5700-clone-reference.sh @@ -51,7 +51,7 @@ diff expected current' cd "$base_dir" test_expect_success 'cloning with reference (no -l -s)' \ -'git clone --reference B file://`pwd`/A D' +'git clone --reference B "file://$(pwd)/A" D' cd "$base_dir" diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh index 910ccb4fff..ef7127c1b3 100755 --- a/t/t5710-info-alternate.sh +++ b/t/t5710-info-alternate.sh @@ -81,9 +81,9 @@ test_valid_repo' cd "$base_dir" test_expect_success 'breaking of loops' \ -"echo '$base_dir/B/.git/objects' >> '$base_dir'/A/.git/objects/info/alternates&& +'echo "$base_dir"/B/.git/objects >> "$base_dir"/A/.git/objects/info/alternates&& cd C && -test_valid_repo" +test_valid_repo' cd "$base_dir" diff --git a/t/t6000lib.sh b/t/t6000lib.sh index c0baaa5360..f55627b641 100755 --- a/t/t6000lib.sh +++ b/t/t6000lib.sh @@ -49,13 +49,15 @@ as_author() shift 1 _save=$GIT_AUTHOR_EMAIL - export GIT_AUTHOR_EMAIL="$_author" + GIT_AUTHOR_EMAIL="$_author" + export GIT_AUTHOR_EMAIL "$@" if test -z "$_save" then unset GIT_AUTHOR_EMAIL else - export GIT_AUTHOR_EMAIL="$_save" + GIT_AUTHOR_EMAIL="$_save" + export GIT_AUTHOR_EMAIL fi } @@ -69,7 +71,8 @@ on_committer_date() { _date=$1 shift 1 - export GIT_COMMITTER_DATE="$_date" + GIT_COMMITTER_DATE="$_date" + export GIT_COMMITTER_DATE "$@" unset GIT_COMMITTER_DATE } diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh index 96f3d35530..b6e57b2426 100755 --- a/t/t6010-merge-base.sh +++ b/t/t6010-merge-base.sh @@ -13,10 +13,11 @@ T=$(git write-tree) M=1130000000 Z=+0000 -export GIT_COMMITTER_EMAIL=git@comm.iter.xz -export GIT_COMMITTER_NAME='C O Mmiter' -export GIT_AUTHOR_NAME='A U Thor' -export GIT_AUTHOR_EMAIL=git@au.thor.xz +GIT_COMMITTER_EMAIL=git@comm.iter.xz +GIT_COMMITTER_NAME='C O Mmiter' +GIT_AUTHOR_NAME='A U Thor' +GIT_AUTHOR_EMAIL=git@au.thor.xz +export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL doit() { OFFSET=$1; shift diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 5e3e5445c7..933f567983 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -284,6 +284,31 @@ test_expect_success 'bisect starting with a detached HEAD' ' ' +test_expect_success 'bisect refuses to start if branch bisect exists' ' + git bisect reset && + git branch bisect && + test_must_fail git bisect start && + git branch -d bisect && + git checkout -b bisect && + test_must_fail git bisect start && + git checkout master && + git branch -d bisect +' + +test_expect_success 'bisect refuses to start if branch new-bisect exists' ' + git bisect reset && + git branch new-bisect && + test_must_fail git bisect start && + git branch -d new-bisect +' + +test_expect_success 'bisect errors out if bad and good are mistaken' ' + git bisect reset && + test_must_fail git bisect start $HASH2 $HASH4 2> rev_list_error && + grep "mistake good and bad" rev_list_error && + git bisect reset +' + # # test_done diff --git a/t/t6032-merge-large-rename.sh b/t/t6032-merge-large-rename.sh new file mode 100755 index 0000000000..eac5ebac24 --- /dev/null +++ b/t/t6032-merge-large-rename.sh @@ -0,0 +1,73 @@ +#!/bin/sh + +test_description='merging with large rename matrix' +. ./test-lib.sh + +count() { + i=1 + while test $i -le $1; do + echo $i + i=$(($i + 1)) + done +} + +test_expect_success 'setup (initial)' ' + touch file && + git add . && + git commit -m initial && + git tag initial +' + +make_text() { + echo $1: $2 + for i in `count 20`; do + echo $1: $i + done + echo $1: $3 +} + +test_rename() { + test_expect_success "rename ($1, $2)" ' + n='$1' + expect='$2' + git checkout -f master && + git branch -D test$n || true && + git reset --hard initial && + for i in $(count $n); do + make_text $i initial initial >$i + done && + git add . && + git commit -m add=$n && + for i in $(count $n); do + make_text $i changed initial >$i + done && + git commit -a -m change=$n && + git checkout -b test$n HEAD^ && + for i in $(count $n); do + git rm $i + make_text $i initial changed >$i.moved + done && + git add . && + git commit -m change+rename=$n && + case "$expect" in + ok) git merge master ;; + *) test_must_fail git merge master ;; + esac + ' +} + +test_rename 5 ok + +test_expect_success 'set diff.renamelimit to 4' ' + git config diff.renamelimit 4 +' +test_rename 4 ok +test_rename 5 fail + +test_expect_success 'set merge.renamelimit to 5' ' + git config merge.renamelimit 5 +' +test_rename 5 ok +test_rename 6 fail + +test_done diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh index 526d7d1c44..c9bf6fdba3 100755 --- a/t/t6200-fmt-merge-msg.sh +++ b/t/t6200-fmt-merge-msg.sh @@ -82,14 +82,14 @@ test_expect_success 'merge-msg test #1' ' git diff actual expected ' -cat >expected <<\EOF -Merge branch 'left' of ../trash +cat >expected <<EOF +Merge branch 'left' of ../$test EOF test_expect_success 'merge-msg test #2' ' git checkout master && - git fetch ../trash left && + git fetch ../"$test" left && git fmt-merge-msg <.git/FETCH_HEAD >actual && git diff actual expected @@ -106,8 +106,24 @@ Merge branch 'left' Common #1 EOF -test_expect_success 'merge-msg test #3' ' +test_expect_success 'merge-msg test #3-1' ' + + git config --unset-all merge.log + git config --unset-all merge.summary + git config merge.log true && + + git checkout master && + setdate && + git fetch . left && + + git fmt-merge-msg <.git/FETCH_HEAD >actual && + git diff actual expected +' + +test_expect_success 'merge-msg test #3-2' ' + git config --unset-all merge.log + git config --unset-all merge.summary git config merge.summary true && git checkout master && @@ -136,8 +152,24 @@ Merge branches 'left' and 'right' Common #1 EOF -test_expect_success 'merge-msg test #4' ' +test_expect_success 'merge-msg test #4-1' ' + + git config --unset-all merge.log + git config --unset-all merge.summary + git config merge.log true && + + git checkout master && + setdate && + git fetch . left right && + git fmt-merge-msg <.git/FETCH_HEAD >actual && + git diff actual expected +' + +test_expect_success 'merge-msg test #4-2' ' + + git config --unset-all merge.log + git config --unset-all merge.summary git config merge.summary true && git checkout master && @@ -148,8 +180,24 @@ test_expect_success 'merge-msg test #4' ' git diff actual expected ' -test_expect_success 'merge-msg test #5' ' +test_expect_success 'merge-msg test #5-1' ' + + git config --unset-all merge.log + git config --unset-all merge.summary + git config merge.log yes && + + git checkout master && + setdate && + git fetch . left right && + + git fmt-merge-msg <.git/FETCH_HEAD >actual && + git diff actual expected +' + +test_expect_success 'merge-msg test #5-2' ' + git config --unset-all merge.log + git config --unset-all merge.summary git config merge.summary yes && git checkout master && diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index 16df3d4adb..1639c7aa96 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -125,7 +125,7 @@ test_expect_success 'use index-filter to move into a subdirectory' ' "git ls-files -s | sed \"s-\\t-&newsubdir/-\" | GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \ git update-index --index-info && - mv \$GIT_INDEX_FILE.new \$GIT_INDEX_FILE" directorymoved && + mv \"\$GIT_INDEX_FILE.new\" \"\$GIT_INDEX_FILE\"" directorymoved && test -z "$(git diff HEAD directorymoved:newsubdir)"' test_expect_success 'stops when msg filter fails' ' diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 1a7141ecd7..2dcee7ccc5 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -626,7 +626,8 @@ esac cp -R ../t7004 ./gpghome chmod 0700 gpghome -export GNUPGHOME="$(pwd)/gpghome" +GNUPGHOME="$(pwd)/gpghome" +export GNUPGHOME get_tag_header signed-tag $commit commit $time >expect echo 'A signed tag message' >>expect diff --git a/t/t7010-setup.sh b/t/t7010-setup.sh index 02cf7c5c9d..d8a7c79852 100755 --- a/t/t7010-setup.sh +++ b/t/t7010-setup.sh @@ -122,7 +122,7 @@ test_expect_success 'commit using absolute path names' ' test_expect_success 'log using absolute path names' ' echo bb >>a/b/c/d && - git commit -m "bb" $(pwd)/a/b/c/d && + git commit -m "bb" "$(pwd)/a/b/c/d" && git log a/b/c/d >f1.txt && git log "$(pwd)/a/b/c/d" >f2.txt && diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index a50492f7c0..bd77239708 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -112,7 +112,7 @@ test_expect_success 'git-clean with absolute path' ' touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && would_clean=$( cd docs && - git clean -n $(pwd)/../src | + git clean -n "$(pwd)/../src" | sed -n -e "s|^Would remove ||p" ) && test "$would_clean" = ../src/part3.c || { diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index c0288f345f..d3370ff7ff 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -41,7 +41,7 @@ test_expect_success \ test_expect_success \ "using paths with --interactive" \ "echo bong-o-bong >file && - ! echo 7 | git-commit -m foo --interactive file" + ! (echo 7 | git-commit -m foo --interactive file)" test_expect_success \ "using invalid commit with -C" \ @@ -79,8 +79,8 @@ test_expect_success \ cat >editor <<\EOF #!/bin/sh -sed -e "s/a file/an amend commit/g" < $1 > $1- -mv $1- $1 +sed -e "s/a file/an amend commit/g" < "$1" > "$1-" +mv "$1-" "$1" EOF chmod 755 editor @@ -99,8 +99,8 @@ test_expect_success \ cat >editor <<\EOF #!/bin/sh -sed -e "s/amend/older/g" < $1 > $1- -mv $1- $1 +sed -e "s/amend/older/g" < "$1" > "$1-" +mv "$1-" "$1" EOF chmod 755 editor diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh index eff36aaee3..88577af953 100755 --- a/t/t7504-commit-msg-hook.sh +++ b/t/t7504-commit-msg-hook.sh @@ -19,6 +19,9 @@ cp FAKE_MSG "$1" exit 0 EOF chmod +x fake-editor + +## Not using test_set_editor here so we can easily ensure the editor variable +## is only set for the editor tests FAKE_EDITOR="$(pwd)/fake-editor" export FAKE_EDITOR @@ -27,7 +30,7 @@ test_expect_success 'with no hook (editor)' ' echo "more foo" >> file && git add file && echo "more foo" > FAKE_MSG && - GIT_EDITOR="$FAKE_EDITOR" git commit + GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit ' @@ -44,7 +47,7 @@ test_expect_success '--no-verify with no hook (editor)' ' echo "more bar" > file && git add file && echo "more bar" > FAKE_MSG && - GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify + GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify ' @@ -71,7 +74,7 @@ test_expect_success 'with succeeding hook (editor)' ' echo "more more" >> file && git add file && echo "more more" > FAKE_MSG && - GIT_EDITOR="$FAKE_EDITOR" git commit + GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit ' @@ -88,7 +91,7 @@ test_expect_success '--no-verify with succeeding hook (editor)' ' echo "even more more" >> file && git add file && echo "even more more" > FAKE_MSG && - GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify + GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify ' @@ -111,7 +114,7 @@ test_expect_success 'with failing hook (editor)' ' echo "more another" >> file && git add file && echo "more another" > FAKE_MSG && - ! (GIT_EDITOR="$FAKE_EDITOR" git commit) + ! (GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit) ' @@ -128,7 +131,7 @@ test_expect_success '--no-verify with failing hook (editor)' ' echo "more stuff" >> file && git add file && echo "more stuff" > FAKE_MSG && - GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify + GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify ' @@ -146,7 +149,7 @@ test_expect_success 'with non-executable hook (editor)' ' echo "content again" >> file && git add file && echo "content again" > FAKE_MSG && - GIT_EDITOR="$FAKE_EDITOR" git commit -m "content again" + GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -m "content again" ' @@ -163,7 +166,7 @@ test_expect_success '--no-verify with non-executable hook (editor)' ' echo "even more content" >> file && git add file && echo "even more content" > FAKE_MSG && - GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify + GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify ' @@ -193,7 +196,7 @@ test_expect_success 'hook edits commit message (editor)' ' echo "additional content" >> file && git add file && echo "additional content" > FAKE_MSG && - GIT_EDITOR="$FAKE_EDITOR" git commit && + GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit && commit_msg_is "new message" ' @@ -212,7 +215,7 @@ test_expect_success "hook doesn't edit commit message (editor)" ' echo "more plus" >> file && git add file && echo "more plus" > FAKE_MSG && - GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify && + GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify && commit_msg_is "more plus" ' diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh index 802aa624d0..cd6c7c8342 100755 --- a/t/t7505-prepare-commit-msg-hook.sh +++ b/t/t7505-prepare-commit-msg-hook.sh @@ -18,6 +18,9 @@ cat > fake-editor <<'EOF' exit 0 EOF chmod +x fake-editor + +## Not using test_set_editor here so we can easily ensure the editor variable +## is only set for the editor tests FAKE_EDITOR="$(pwd)/fake-editor" export FAKE_EDITOR @@ -58,7 +61,7 @@ test_expect_success 'with hook (-m editor)' ' echo "more" >> file && git add file && - GIT_EDITOR="$FAKE_EDITOR" git commit -e -m "more more" && + GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -e -m "more more" && test "`git log -1 --pretty=format:%s`" = message ' @@ -85,7 +88,7 @@ test_expect_success 'with hook (-F editor)' ' echo "more" >> file && git add file && - (echo more more | GIT_EDITOR="$FAKE_EDITOR" git commit -e -F -) && + (echo more more | GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -e -F -) && test "`git log -1 --pretty=format:%s`" = message ' @@ -104,7 +107,7 @@ test_expect_success 'with hook (editor)' ' echo "more more" >> file && git add file && - GIT_EDITOR="$FAKE_EDITOR" git commit && + GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit && test "`git log -1 --pretty=format:%s`" = default ' @@ -114,7 +117,7 @@ test_expect_success 'with hook (--amend)' ' head=`git rev-parse HEAD` && echo "more" >> file && git add file && - GIT_EDITOR="$FAKE_EDITOR" git commit --amend && + GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --amend && test "`git log -1 --pretty=format:%s`" = "$head" ' @@ -124,7 +127,7 @@ test_expect_success 'with hook (-c)' ' head=`git rev-parse HEAD` && echo "more" >> file && git add file && - GIT_EDITOR="$FAKE_EDITOR" git commit -c $head && + GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head && test "`git log -1 --pretty=format:%s`" = "$head" ' @@ -139,7 +142,7 @@ test_expect_success 'with failing hook' ' head=`git rev-parse HEAD` && echo "more" >> file && git add file && - ! GIT_EDITOR="$FAKE_EDITOR" git commit -c $head + ! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head ' @@ -148,7 +151,7 @@ test_expect_success 'with failing hook (--no-verify)' ' head=`git rev-parse HEAD` && echo "more" >> file && git add file && - ! GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify -c $head + ! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify -c $head ' diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh new file mode 100755 index 0000000000..a75130cdbb --- /dev/null +++ b/t/t7506-status-submodule.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +test_description='git-status for submodule' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_create_repo sub + cd sub && + : >bar && + git add bar && + git commit -m " Add bar" && + cd .. && + git add sub && + git commit -m "Add submodule sub" +' + +test_expect_success 'status clean' ' + git status | + grep "nothing to commit" +' +test_expect_success 'status -a clean' ' + git status -a | + grep "nothing to commit" +' +test_expect_success 'rm submodule contents' ' + rm -rf sub/* sub/.git +' +test_expect_success 'status clean (empty submodule dir)' ' + git status | + grep "nothing to commit" +' +test_expect_success 'status -a clean (empty submodule dir)' ' + git status -a | + grep "nothing to commit" +' + +test_done diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 56869aceed..d21cd290d3 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -104,7 +104,11 @@ create_merge_msgs() { git log --no-merges ^HEAD c2 >>squash.1-5 && echo "Squashed commit of the following:" >squash.1-5-9 && echo >>squash.1-5-9 && - git log --no-merges ^HEAD c2 c3 >>squash.1-5-9 + git log --no-merges ^HEAD c2 c3 >>squash.1-5-9 && + echo > msg.nolog && + echo "* commit 'c3':" >msg.log && + echo " commit 3" >>msg.log && + echo >>msg.log } verify_diff() { @@ -364,7 +368,7 @@ test_expect_success 'merge c1 with c2 (squash in config)' ' test_debug 'gitk --all' -test_expect_success 'override config option -n' ' +test_expect_success 'override config option -n with --summary' ' git reset --hard c1 && git config branch.master.mergeoptions "-n" && test_tick && @@ -373,15 +377,30 @@ test_expect_success 'override config option -n' ' verify_parents $c1 $c2 && if ! grep "^ file | *2 +-$" diffstat.txt then - echo "[OOPS] diffstat was not generated" + echo "[OOPS] diffstat was not generated with --summary" + false + fi +' + +test_expect_success 'override config option -n with --stat' ' + git reset --hard c1 && + git config branch.master.mergeoptions "-n" && + test_tick && + git merge --stat c2 >diffstat.txt && + verify_merge file result.1-5 msg.1-5 && + verify_parents $c1 $c2 && + if ! grep "^ file | *2 +-$" diffstat.txt + then + echo "[OOPS] diffstat was not generated with --stat" + false fi ' test_debug 'gitk --all' -test_expect_success 'override config option --summary' ' +test_expect_success 'override config option --stat' ' git reset --hard c1 && - git config branch.master.mergeoptions "--summary" && + git config branch.master.mergeoptions "--stat" && test_tick && git merge -n c2 >diffstat.txt && verify_merge file result.1-5 msg.1-5 && @@ -441,6 +460,16 @@ test_expect_success 'merge c0 with c1 (ff overrides no-ff)' ' verify_head $c1 ' +test_expect_success 'merge log message' ' + git reset --hard c0 && + git merge --no-log c2 && + git show -s --pretty=format:%b HEAD >msg.act && + verify_diff msg.nolog msg.act "[OOPS] bad merge log message" && + git merge --log c3 && + git show -s --pretty=format:%b HEAD >msg.act && + verify_diff msg.log msg.act "[OOPS] bad merge log message" +' + test_debug 'gitk --all' test_done diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index c0973b4e6e..04baa61c04 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -139,15 +139,16 @@ test_expect_success 'Valid In-Reply-To when prompting' ' test_expect_success 'setup fake editor' ' (echo "#!/bin/sh" && - echo "echo fake edit >>\$1" + echo "echo fake edit >>\"\$1\"" ) >fake-editor && chmod +x fake-editor ' +test_set_editor "$(pwd)/fake-editor" + test_expect_success '--compose works' ' clean_fake_sendmail && echo y | \ - GIT_EDITOR=$(pwd)/fake-editor \ GIT_SEND_EMAIL_NOTTY=1 \ git send-email \ --compose --subject foo \ @@ -166,4 +167,70 @@ test_expect_success 'second message is patch' ' grep "Subject:.*Second" msgtxt2 ' +cat >expected-show-all-headers <<\EOF +0001-Second.patch +(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' +Dry-OK. Log says: +Server: relay.example.com +MAIL FROM:<from@example.com> +RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com> +From: Example <from@example.com> +To: to@example.com +Cc: cc@example.com, A <author@example.com> +Subject: [PATCH 1/1] Second. +Date: DATE-STRING +Message-Id: MESSAGE-ID-STRING +X-Mailer: X-MAILER-STRING + +Result: OK +EOF + +test_expect_success 'sendemail.cc set' ' + git config sendemail.cc cc@example.com && + git send-email \ + --dry-run \ + --from="Example <from@example.com>" \ + --to=to@example.com \ + --smtp-server relay.example.com \ + $patches | + sed -e "s/^\(Date:\).*/\1 DATE-STRING/" \ + -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \ + -e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \ + >actual-show-all-headers && + test_cmp expected-show-all-headers actual-show-all-headers +' + +cat >expected-show-all-headers <<\EOF +0001-Second.patch +(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' +Dry-OK. Log says: +Server: relay.example.com +MAIL FROM:<from@example.com> +RCPT TO:<to@example.com>,<author@example.com> +From: Example <from@example.com> +To: to@example.com +Cc: A <author@example.com> +Subject: [PATCH 1/1] Second. +Date: DATE-STRING +Message-Id: MESSAGE-ID-STRING +X-Mailer: X-MAILER-STRING + +Result: OK +EOF + +test_expect_success 'sendemail.cc unset' ' + git config --unset sendemail.cc && + git send-email \ + --dry-run \ + --from="Example <from@example.com>" \ + --to=to@example.com \ + --smtp-server relay.example.com \ + $patches | + sed -e "s/^\(Date:\).*/\1 DATE-STRING/" \ + -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \ + -e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \ + >actual-show-all-headers && + test_cmp expected-show-all-headers actual-show-all-headers +' + test_done diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh index 4e24ab3a7d..bdf29c1734 100755 --- a/t/t9100-git-svn-basic.sh +++ b/t/t9100-git-svn-basic.sh @@ -20,39 +20,39 @@ esac echo 'define NO_SVN_TESTS to skip git-svn tests' test_expect_success \ - 'initialize git-svn' " + 'initialize git-svn' ' mkdir import && cd import && echo foo > foo && ln -s foo foo.link mkdir -p dir/a/b/c/d/e && - echo 'deep dir' > dir/a/b/c/d/e/file && + echo "deep dir" > dir/a/b/c/d/e/file && mkdir bar && - echo 'zzz' > bar/zzz && - echo '#!/bin/sh' > exec.sh && + echo "zzz" > bar/zzz && + echo "#!/bin/sh" > exec.sh && chmod +x exec.sh && - svn import -m 'import for git-svn' . $svnrepo >/dev/null && + svn import -m "import for git-svn" . "$svnrepo" >/dev/null && cd .. && rm -rf import && - git-svn init $svnrepo" + git-svn init "$svnrepo"' test_expect_success \ 'import an SVN revision into git' \ 'git-svn fetch' -test_expect_success "checkout from svn" "svn co $svnrepo '$SVN_TREE'" +test_expect_success "checkout from svn" 'svn co "$svnrepo" "$SVN_TREE"' name='try a deep --rmdir with a commit' -test_expect_success "$name" " +test_expect_success "$name" ' git checkout -f -b mybranch remotes/git-svn && mv dir/a/b/c/d/e/file dir/file && cp dir/file file && git update-index --add --remove dir/a/b/c/d/e/file dir/file file && - git commit -m '$name' && + git commit -m "$name" && git-svn set-tree --find-copies-harder --rmdir \ remotes/git-svn..mybranch && - svn up '$SVN_TREE' && - test -d '$SVN_TREE'/dir && test ! -d '$SVN_TREE'/dir/a" + svn up "$SVN_TREE" && + test -d "$SVN_TREE"/dir && test ! -d "$SVN_TREE"/dir/a' name='detect node change from file to directory #1' @@ -68,108 +68,108 @@ test_expect_success "$name" " name='detect node change from directory to file #1' -test_expect_success "$name" " - rm -rf dir '$GIT_DIR'/index && +test_expect_success "$name" ' + rm -rf dir "$GIT_DIR"/index && git checkout -f -b mybranch2 remotes/git-svn && mv bar/zzz zzz && rm -rf bar && mv zzz bar && git update-index --remove -- bar/zzz && git update-index --add -- bar && - git commit -m '$name' && + git commit -m "$name" && ! git-svn set-tree --find-copies-harder --rmdir \ - remotes/git-svn..mybranch2" || true + remotes/git-svn..mybranch2' || true name='detect node change from file to directory #2' -test_expect_success "$name" " - rm -f '$GIT_DIR'/index && +test_expect_success "$name" ' + rm -f "$GIT_DIR"/index && git checkout -f -b mybranch3 remotes/git-svn && rm bar/zzz && git update-index --remove bar/zzz && mkdir bar/zzz && echo yyy > bar/zzz/yyy && git update-index --add bar/zzz/yyy && - git commit -m '$name' && + git commit -m "$name" && ! git-svn set-tree --find-copies-harder --rmdir \ - remotes/git-svn..mybranch3" || true + remotes/git-svn..mybranch3' || true name='detect node change from directory to file #2' -test_expect_success "$name" " - rm -f '$GIT_DIR'/index && +test_expect_success "$name" ' + rm -f "$GIT_DIR"/index && git checkout -f -b mybranch4 remotes/git-svn && rm -rf dir && git update-index --remove -- dir/file && touch dir && echo asdf > dir && git update-index --add -- dir && - git commit -m '$name' && + git commit -m "$name" && ! git-svn set-tree --find-copies-harder --rmdir \ - remotes/git-svn..mybranch4" || true + remotes/git-svn..mybranch4' || true name='remove executable bit from a file' -test_expect_success "$name" " - rm -f '$GIT_DIR'/index && +test_expect_success "$name" ' + rm -f "$GIT_DIR"/index && git checkout -f -b mybranch5 remotes/git-svn && chmod -x exec.sh && git update-index exec.sh && - git commit -m '$name' && + git commit -m "$name" && git-svn set-tree --find-copies-harder --rmdir \ remotes/git-svn..mybranch5 && - svn up '$SVN_TREE' && - test ! -x '$SVN_TREE'/exec.sh" + svn up "$SVN_TREE" && + test ! -x "$SVN_TREE"/exec.sh' name='add executable bit back file' -test_expect_success "$name" " +test_expect_success "$name" ' chmod +x exec.sh && git update-index exec.sh && - git commit -m '$name' && + git commit -m "$name" && git-svn set-tree --find-copies-harder --rmdir \ remotes/git-svn..mybranch5 && - svn up '$SVN_TREE' && - test -x '$SVN_TREE'/exec.sh" + svn up "$SVN_TREE" && + test -x "$SVN_TREE"/exec.sh' name='executable file becomes a symlink to bar/zzz (file)' -test_expect_success "$name" " +test_expect_success "$name" ' rm exec.sh && ln -s bar/zzz exec.sh && git update-index exec.sh && - git commit -m '$name' && + git commit -m "$name" && git-svn set-tree --find-copies-harder --rmdir \ remotes/git-svn..mybranch5 && - svn up '$SVN_TREE' && - test -L '$SVN_TREE'/exec.sh" + svn up "$SVN_TREE" && + test -L "$SVN_TREE"/exec.sh' name='new symlink is added to a file that was also just made executable' -test_expect_success "$name" " +test_expect_success "$name" ' chmod +x bar/zzz && ln -s bar/zzz exec-2.sh && git update-index --add bar/zzz exec-2.sh && - git commit -m '$name' && + git commit -m "$name" && git-svn set-tree --find-copies-harder --rmdir \ remotes/git-svn..mybranch5 && - svn up '$SVN_TREE' && - test -x '$SVN_TREE'/bar/zzz && - test -L '$SVN_TREE'/exec-2.sh" + svn up "$SVN_TREE" && + test -x "$SVN_TREE"/bar/zzz && + test -L "$SVN_TREE"/exec-2.sh' name='modify a symlink to become a file' -test_expect_success "$name" " +test_expect_success "$name" ' echo git help > help || true && rm exec-2.sh && cp help exec-2.sh && git update-index exec-2.sh && - git commit -m '$name' && + git commit -m "$name" && git-svn set-tree --find-copies-harder --rmdir \ remotes/git-svn..mybranch5 && - svn up '$SVN_TREE' && - test -f '$SVN_TREE'/exec-2.sh && - test ! -L '$SVN_TREE'/exec-2.sh && - git diff help $SVN_TREE/exec-2.sh" + svn up "$SVN_TREE" && + test -f "$SVN_TREE"/exec-2.sh && + test ! -L "$SVN_TREE"/exec-2.sh && + git diff help "$SVN_TREE"/exec-2.sh' if test "$have_utf8" = t then @@ -190,10 +190,10 @@ name='test fetch functionality (svn => git) with alternate GIT_SVN_ID' GIT_SVN_ID=alt export GIT_SVN_ID test_expect_success "$name" \ - "git-svn init $svnrepo && git-svn fetch && + 'git-svn init "$svnrepo" && git-svn fetch && git rev-list --pretty=raw remotes/git-svn | grep ^tree | uniq > a && git rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b && - git diff a b" + git diff a b' name='check imported tree checksums expected tree checksums' rm -f expected @@ -219,22 +219,22 @@ test_expect_success 'exit if remote refs are ambigious' " ! git-svn migrate " -test_expect_success 'exit if init-ing a would clobber a URL' " - svnadmin create ${PWD}/svnrepo2 && - svn mkdir -m 'mkdir bar' ${svnrepo}2/bar && +test_expect_success 'exit if init-ing a would clobber a URL' ' + svnadmin create "${PWD}/svnrepo2" && + svn mkdir -m "mkdir bar" "${svnrepo}2/bar" && git config --unset svn-remote.svn.fetch \ - '^bar:refs/remotes/git-svn$' && - ! git-svn init ${svnrepo}2/bar - " + "^bar:refs/remotes/git-svn$" && + ! git-svn init "${svnrepo}2/bar" + ' test_expect_success \ - 'init allows us to connect to another directory in the same repo' " - git-svn init --minimize-url -i bar $svnrepo/bar && + 'init allows us to connect to another directory in the same repo' ' + git-svn init --minimize-url -i bar "$svnrepo/bar" && git config --get svn-remote.svn.fetch \ - '^bar:refs/remotes/bar$' && + "^bar:refs/remotes/bar$" && git config --get svn-remote.svn.fetch \ - '^:refs/remotes/git-svn$' - " + "^:refs/remotes/git-svn$" + ' test_expect_success 'able to dcommit to a subdirectory' " git-svn fetch -i bar && diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh index d7a704754e..f420796c31 100755 --- a/t/t9101-git-svn-props.sh +++ b/t/t9101-git-svn-props.sh @@ -52,7 +52,7 @@ EOF cd .. rm -rf import -test_expect_success 'checkout working copy from svn' "svn co $svnrepo test_wc" +test_expect_success 'checkout working copy from svn' 'svn co "$svnrepo" test_wc' test_expect_success 'setup some commits to svn' \ 'cd test_wc && echo Greetings >> kw.c && @@ -66,7 +66,7 @@ test_expect_success 'setup some commits to svn' \ svn commit -m "Propset Id" && cd ..' -test_expect_success 'initialize git-svn' "git-svn init $svnrepo" +test_expect_success 'initialize git-svn' 'git-svn init "$svnrepo"' test_expect_success 'fetch revisions from svn' 'git-svn fetch' name='test svn:keywords ignoring' @@ -90,9 +90,9 @@ test_expect_success "propset CR on crlf files" \ cd ..' test_expect_success 'fetch and pull latest from svn and checkout a new wc' \ - "git-svn fetch && + 'git-svn fetch && git pull . remotes/git-svn && - svn co $svnrepo new_wc" + svn co "$svnrepo" new_wc' for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf do diff --git a/t/t9102-git-svn-deep-rmdir.sh b/t/t9102-git-svn-deep-rmdir.sh index 4e0808380f..0e7ce34b9b 100755 --- a/t/t9102-git-svn-deep-rmdir.sh +++ b/t/t9102-git-svn-deep-rmdir.sh @@ -2,29 +2,29 @@ test_description='git-svn rmdir' . ./lib-git-svn.sh -test_expect_success 'initialize repo' " +test_expect_success 'initialize repo' ' mkdir import && cd import && mkdir -p deeply/nested/directory/number/1 && mkdir -p deeply/nested/directory/number/2 && echo foo > deeply/nested/directory/number/1/file && echo foo > deeply/nested/directory/number/2/another && - svn import -m 'import for git-svn' . $svnrepo && + svn import -m "import for git-svn" . "$svnrepo" && cd .. - " + ' -test_expect_success 'mirror via git-svn' " - git-svn init $svnrepo && +test_expect_success 'mirror via git-svn' ' + git-svn init "$svnrepo" && git-svn fetch && git checkout -f -b test-rmdir remotes/git-svn - " + ' -test_expect_success 'Try a commit on rmdir' " +test_expect_success 'Try a commit on rmdir' ' git rm -f deeply/nested/directory/number/2/another && - git commit -a -m 'remove another' && + git commit -a -m "remove another" && git-svn set-tree --rmdir HEAD && - svn ls -R $svnrepo | grep ^deeply/nested/directory/number/1 - " + svn ls -R "$svnrepo" | grep ^deeply/nested/directory/number/1 + ' test_done diff --git a/t/t9103-git-svn-tracked-directory-removed.sh b/t/t9103-git-svn-tracked-directory-removed.sh index 0f0b0fd2c6..9ffd8458ef 100755 --- a/t/t9103-git-svn-tracked-directory-removed.sh +++ b/t/t9103-git-svn-tracked-directory-removed.sh @@ -10,30 +10,30 @@ test_expect_success 'make history for tracking' ' mkdir import && mkdir import/trunk && echo hello >> import/trunk/README && - svn import -m initial import $svnrepo && + svn import -m initial import "$svnrepo" && rm -rf import && - svn co $svnrepo/trunk trunk && + svn co "$svnrepo"/trunk trunk && echo bye bye >> trunk/README && - svn rm -m "gone" $svnrepo/trunk && + svn rm -m "gone" "$svnrepo"/trunk && rm -rf trunk && mkdir trunk && echo "new" > trunk/FOLLOWME && - svn import -m "new trunk" trunk $svnrepo/trunk + svn import -m "new trunk" trunk "$svnrepo"/trunk ' test_expect_success 'clone repo with git' ' - git svn clone -s $svnrepo x && + git svn clone -s "$svnrepo" x && test -f x/FOLLOWME && test ! -f x/README ' -test_expect_success 'make sure r2 still has old file' ' +test_expect_success 'make sure r2 still has old file' " cd x && - test -n "$(git svn find-rev r1)" && - git reset --hard $(git svn find-rev r1) && + test -n \"\$(git svn find-rev r1)\" && + git reset --hard \$(git svn find-rev r1) && test -f README && test ! -f FOLLOWME && - test x$(git svn find-rev r2) = x -' + test x\$(git svn find-rev r2) = x +" test_done diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh index 7ba76309ac..4d964e2db7 100755 --- a/t/t9104-git-svn-follow-parent.sh +++ b/t/t9104-git-svn-follow-parent.sh @@ -6,165 +6,165 @@ test_description='git-svn fetching' . ./lib-git-svn.sh -test_expect_success 'initialize repo' " +test_expect_success 'initialize repo' ' mkdir import && cd import && mkdir -p trunk && echo hello > trunk/readme && - svn import -m 'initial' . $svnrepo && + svn import -m "initial" . "$svnrepo" && cd .. && - svn co $svnrepo wc && + svn co "$svnrepo" wc && cd wc && echo world >> trunk/readme && poke trunk/readme && - svn commit -m 'another commit' && + svn commit -m "another commit" && svn up && svn mv trunk thunk && echo goodbye >> thunk/readme && poke thunk/readme && - svn commit -m 'bye now' && + svn commit -m "bye now" && cd .. - " + ' -test_expect_success 'init and fetch a moved directory' " - git-svn init --minimize-url -i thunk $svnrepo/thunk && +test_expect_success 'init and fetch a moved directory' ' + git-svn init --minimize-url -i thunk "$svnrepo"/thunk && git-svn fetch -i thunk && - test \"\`git rev-parse --verify refs/remotes/thunk@2\`\" \ - = \"\`git rev-parse --verify refs/remotes/thunk~1\`\" && - test \"\`git cat-file blob refs/remotes/thunk:readme |\ - sed -n -e '3p'\`\" = goodbye && - test -z \"\`git config --get svn-remote.svn.fetch \ - '^trunk:refs/remotes/thunk@2$'\`\" - " + test "`git rev-parse --verify refs/remotes/thunk@2`" \ + = "`git rev-parse --verify refs/remotes/thunk~1`" && + test "`git cat-file blob refs/remotes/thunk:readme |\ + sed -n -e "3p"`" = goodbye && + test -z "`git config --get svn-remote.svn.fetch \ + "^trunk:refs/remotes/thunk@2$"`" + ' -test_expect_success 'init and fetch from one svn-remote' " - git config svn-remote.svn.url $svnrepo && +test_expect_success 'init and fetch from one svn-remote' ' + git config svn-remote.svn.url "$svnrepo" && git config --add svn-remote.svn.fetch \ trunk:refs/remotes/svn/trunk && git config --add svn-remote.svn.fetch \ thunk:refs/remotes/svn/thunk && git-svn fetch -i svn/thunk && - test \"\`git rev-parse --verify refs/remotes/svn/trunk\`\" \ - = \"\`git rev-parse --verify refs/remotes/svn/thunk~1\`\" && - test \"\`git cat-file blob refs/remotes/svn/thunk:readme |\ - sed -n -e '3p'\`\" = goodbye - " + test "`git rev-parse --verify refs/remotes/svn/trunk`" \ + = "`git rev-parse --verify refs/remotes/svn/thunk~1`" && + test "`git cat-file blob refs/remotes/svn/thunk:readme |\ + sed -n -e "3p"`" = goodbye + ' -test_expect_success 'follow deleted parent' " - (svn cp -m 'resurrecting trunk as junk' \ - $svnrepo/trunk@2 $svnrepo/junk || - svn cp -m 'resurrecting trunk as junk' \ - -r2 $svnrepo/trunk $svnrepo/junk) && +test_expect_success 'follow deleted parent' ' + (svn cp -m "resurrecting trunk as junk" \ + "$svnrepo"/trunk@2 "$svnrepo"/junk || + svn cp -m "resurrecting trunk as junk" \ + -r2 "$svnrepo"/trunk "$svnrepo"/junk) && git config --add svn-remote.svn.fetch \ junk:refs/remotes/svn/junk && git-svn fetch -i svn/thunk && git-svn fetch -i svn/junk && - test -z \"\`git diff svn/junk svn/trunk\`\" && - test \"\`git merge-base svn/junk svn/trunk\`\" \ - = \"\`git rev-parse svn/trunk\`\" - " + test -z "`git diff svn/junk svn/trunk`" && + test "`git merge-base svn/junk svn/trunk`" \ + = "`git rev-parse svn/trunk`" + ' -test_expect_success 'follow larger parent' " +test_expect_success 'follow larger parent' ' mkdir -p import/trunk/thunk/bump/thud && echo hi > import/trunk/thunk/bump/thud/file && - svn import -m 'import a larger parent' import $svnrepo/larger-parent && - svn cp -m 'hi' $svnrepo/larger-parent $svnrepo/another-larger && + svn import -m "import a larger parent" import "$svnrepo"/larger-parent && + svn cp -m "hi" "$svnrepo"/larger-parent "$svnrepo"/another-larger && git-svn init --minimize-url -i larger \ - $svnrepo/another-larger/trunk/thunk/bump/thud && + "$svnrepo"/another-larger/trunk/thunk/bump/thud && git-svn fetch -i larger && git rev-parse --verify refs/remotes/larger && git rev-parse --verify \ refs/remotes/larger-parent/trunk/thunk/bump/thud && - test \"\`git merge-base \ + test "`git merge-base \ refs/remotes/larger-parent/trunk/thunk/bump/thud \ - refs/remotes/larger\`\" = \ - \"\`git rev-parse refs/remotes/larger\`\" + refs/remotes/larger`" = \ + "`git rev-parse refs/remotes/larger`" true - " + ' -test_expect_success 'follow higher-level parent' " - svn mkdir -m 'follow higher-level parent' $svnrepo/blob && - svn co $svnrepo/blob blob && +test_expect_success 'follow higher-level parent' ' + svn mkdir -m "follow higher-level parent" "$svnrepo"/blob && + svn co "$svnrepo"/blob blob && cd blob && echo hi > hi && svn add hi && - svn commit -m 'hihi' && + svn commit -m "hihi" && cd .. - svn mkdir -m 'new glob at top level' $svnrepo/glob && - svn mv -m 'move blob down a level' $svnrepo/blob $svnrepo/glob/blob && - git-svn init --minimize-url -i blob $svnrepo/glob/blob && + svn mkdir -m "new glob at top level" "$svnrepo"/glob && + svn mv -m "move blob down a level" "$svnrepo"/blob "$svnrepo"/glob/blob && + git-svn init --minimize-url -i blob "$svnrepo"/glob/blob && git-svn fetch -i blob - " + ' -test_expect_success 'follow deleted directory' " - svn mv -m 'bye!' $svnrepo/glob/blob/hi $svnrepo/glob/blob/bye && - svn rm -m 'remove glob' $svnrepo/glob && - git-svn init --minimize-url -i glob $svnrepo/glob && +test_expect_success 'follow deleted directory' ' + svn mv -m "bye!" "$svnrepo"/glob/blob/hi "$svnrepo"/glob/blob/bye && + svn rm -m "remove glob" "$svnrepo"/glob && + git-svn init --minimize-url -i glob "$svnrepo"/glob && git-svn fetch -i glob && - test \"\`git cat-file blob refs/remotes/glob:blob/bye\`\" = hi && - test \"\`git ls-tree refs/remotes/glob | wc -l \`\" -eq 1 - " + test "`git cat-file blob refs/remotes/glob:blob/bye`" = hi && + test "`git ls-tree refs/remotes/glob | wc -l `" -eq 1 + ' # ref: r9270 of the Subversion repository: (http://svn.collab.net/repos/svn) # in trunk/subversion/bindings/swig/perl -test_expect_success 'follow-parent avoids deleting relevant info' " +test_expect_success 'follow-parent avoids deleting relevant info' ' mkdir -p import/trunk/subversion/bindings/swig/perl/t && for i in a b c ; do \ - echo \$i > import/trunk/subversion/bindings/swig/perl/\$i.pm && - echo _\$i > import/trunk/subversion/bindings/swig/perl/t/\$i.t; \ + echo $i > import/trunk/subversion/bindings/swig/perl/$i.pm && + echo _$i > import/trunk/subversion/bindings/swig/perl/t/$i.t; \ done && - echo 'bad delete test' > \ + echo "bad delete test" > \ import/trunk/subversion/bindings/swig/perl/t/larger-parent && - echo 'bad delete test 2' > \ + echo "bad delete test 2" > \ import/trunk/subversion/bindings/swig/perl/another-larger && cd import && - svn import -m 'r9270 test' . $svnrepo/r9270 && + svn import -m "r9270 test" . "$svnrepo"/r9270 && cd .. && - svn co $svnrepo/r9270/trunk/subversion/bindings/swig/perl r9270 && + svn co "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl r9270 && cd r9270 && svn mkdir native && svn mv t native/t && - for i in a b c; do svn mv \$i.pm native/\$i.pm; done && + for i in a b c; do svn mv $i.pm native/$i.pm; done && echo z >> native/t/c.t && poke native/t/c.t && - svn commit -m 'reorg test' && + svn commit -m "reorg test" && cd .. && git-svn init --minimize-url -i r9270-t \ - $svnrepo/r9270/trunk/subversion/bindings/swig/perl/native/t && + "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl/native/t && git-svn fetch -i r9270-t && - test \`git rev-list r9270-t | wc -l\` -eq 2 && - test \"\`git ls-tree --name-only r9270-t~1\`\" = \ - \"\`git ls-tree --name-only r9270-t\`\" - " + test `git rev-list r9270-t | wc -l` -eq 2 && + test "`git ls-tree --name-only r9270-t~1`" = \ + "`git ls-tree --name-only r9270-t`" + ' -test_expect_success "track initial change if it was only made to parent" " - svn cp -m 'wheee!' $svnrepo/r9270/trunk $svnrepo/r9270/drunk && +test_expect_success "track initial change if it was only made to parent" ' + svn cp -m "wheee!" "$svnrepo"/r9270/trunk "$svnrepo"/r9270/drunk && git-svn init --minimize-url -i r9270-d \ - $svnrepo/r9270/drunk/subversion/bindings/swig/perl/native/t && + "$svnrepo"/r9270/drunk/subversion/bindings/swig/perl/native/t && git-svn fetch -i r9270-d && - test \`git rev-list r9270-d | wc -l\` -eq 3 && - test \"\`git ls-tree --name-only r9270-t\`\" = \ - \"\`git ls-tree --name-only r9270-d\`\" && - test \"\`git rev-parse r9270-t\`\" = \ - \"\`git rev-parse r9270-d~1\`\" - " + test `git rev-list r9270-d | wc -l` -eq 3 && + test "`git ls-tree --name-only r9270-t`" = \ + "`git ls-tree --name-only r9270-d`" && + test "`git rev-parse r9270-t`" = \ + "`git rev-parse r9270-d~1`" + ' -test_expect_success "track multi-parent paths" " - svn cp -m 'resurrect /glob' $svnrepo/r9270 $svnrepo/glob && +test_expect_success "track multi-parent paths" ' + svn cp -m "resurrect /glob" "$svnrepo"/r9270 "$svnrepo"/glob && git-svn multi-fetch && - test \`git cat-file commit refs/remotes/glob | \ - grep '^parent ' | wc -l\` -eq 2 - " + test `git cat-file commit refs/remotes/glob | \ + grep "^parent " | wc -l` -eq 2 + ' test_expect_success "multi-fetch continues to work" " git-svn multi-fetch " -test_expect_success "multi-fetch works off a 'clean' repository" " - rm -r $GIT_DIR/svn $GIT_DIR/refs/remotes $GIT_DIR/logs && - mkdir $GIT_DIR/svn && +test_expect_success "multi-fetch works off a 'clean' repository" ' + rm -r "$GIT_DIR/svn" "$GIT_DIR/refs/remotes" "$GIT_DIR/logs" && + mkdir "$GIT_DIR/svn" && git-svn multi-fetch - " + ' test_debug 'gitk --all &' diff --git a/t/t9105-git-svn-commit-diff.sh b/t/t9105-git-svn-commit-diff.sh index 318e172ef5..63230367bb 100755 --- a/t/t9105-git-svn-commit-diff.sh +++ b/t/t9105-git-svn-commit-diff.sh @@ -4,18 +4,18 @@ test_description='git-svn commit-diff' . ./lib-git-svn.sh -test_expect_success 'initialize repo' " +test_expect_success 'initialize repo' ' mkdir import && cd import && echo hello > readme && - svn import -m 'initial' . $svnrepo && + svn import -m "initial" . "$svnrepo" && cd .. && echo hello > readme && git update-index --add readme && - git commit -a -m 'initial' && + git commit -a -m "initial" && echo world >> readme && - git commit -a -m 'another' - " + git commit -a -m "another" + ' head=`git rev-parse --verify HEAD^0` prev=`git rev-parse --verify HEAD^1` @@ -24,20 +24,20 @@ prev=`git rev-parse --verify HEAD^1` # commit, so only a basic test of functionality is needed since we've # already tested commit extensively elsewhere -test_expect_success 'test the commit-diff command' " - test -n '$prev' && test -n '$head' && - git-svn commit-diff -r1 '$prev' '$head' '$svnrepo' && - svn co $svnrepo wc && +test_expect_success 'test the commit-diff command' ' + test -n "$prev" && test -n "$head" && + git-svn commit-diff -r1 "$prev" "$head" "$svnrepo" && + svn co "$svnrepo" wc && cmp readme wc/readme - " + ' -test_expect_success 'commit-diff to a sub-directory (with git-svn config)' " - svn import -m 'sub-directory' import $svnrepo/subdir && - git-svn init --minimize-url $svnrepo/subdir && +test_expect_success 'commit-diff to a sub-directory (with git-svn config)' ' + svn import -m "sub-directory" import "$svnrepo"/subdir && + git-svn init --minimize-url "$svnrepo"/subdir && git-svn fetch && - git-svn commit-diff -r3 '$prev' '$head' && - svn cat $svnrepo/subdir/readme > readme.2 && + git-svn commit-diff -r3 "$prev" "$head" && + svn cat "$svnrepo"/subdir/readme > readme.2 && cmp readme readme.2 - " + ' test_done diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh index f74ab1269e..58a3a7b1c3 100755 --- a/t/t9106-git-svn-commit-diff-clobber.sh +++ b/t/t9106-git-svn-commit-diff-clobber.sh @@ -4,56 +4,56 @@ test_description='git-svn commit-diff clobber' . ./lib-git-svn.sh -test_expect_success 'initialize repo' " +test_expect_success 'initialize repo' ' mkdir import && cd import && echo initial > file && - svn import -m 'initial' . $svnrepo && + svn import -m "initial" . "$svnrepo" && cd .. && echo initial > file && git update-index --add file && - git commit -a -m 'initial' - " -test_expect_success 'commit change from svn side' " - svn co $svnrepo t.svn && + git commit -a -m "initial" + ' +test_expect_success 'commit change from svn side' ' + svn co "$svnrepo" t.svn && cd t.svn && echo second line from svn >> file && poke file && - svn commit -m 'second line from svn' && + svn commit -m "second line from svn" && cd .. && rm -rf t.svn - " + ' -test_expect_success 'commit conflicting change from git' " +test_expect_success 'commit conflicting change from git' ' echo second line from git >> file && - git commit -a -m 'second line from git' && - ! git-svn commit-diff -r1 HEAD~1 HEAD $svnrepo -" + git commit -a -m "second line from git" && + ! git-svn commit-diff -r1 HEAD~1 HEAD "$svnrepo" +' -test_expect_success 'commit complementing change from git' " +test_expect_success 'commit complementing change from git' ' git reset --hard HEAD~1 && echo second line from svn >> file && - git commit -a -m 'second line from svn' && + git commit -a -m "second line from svn" && echo third line from git >> file && - git commit -a -m 'third line from git' && - git-svn commit-diff -r2 HEAD~1 HEAD $svnrepo - " + git commit -a -m "third line from git" && + git-svn commit-diff -r2 HEAD~1 HEAD "$svnrepo" + ' -test_expect_success 'dcommit fails to commit because of conflict' " - git-svn init $svnrepo && +test_expect_success 'dcommit fails to commit because of conflict' ' + git-svn init "$svnrepo" && git-svn fetch && git reset --hard refs/remotes/git-svn && - svn co $svnrepo t.svn && + svn co "$svnrepo" t.svn && cd t.svn && echo fourth line from svn >> file && poke file && - svn commit -m 'fourth line from svn' && + svn commit -m "fourth line from svn" && cd .. && rm -rf t.svn && - echo 'fourth line from git' >> file && - git commit -a -m 'fourth line from git' && + echo "fourth line from git" >> file && + git commit -a -m "fourth line from git" && ! git-svn dcommit - " + ' test_expect_success 'dcommit does the svn equivalent of an index merge' " git reset --hard refs/remotes/git-svn && @@ -66,15 +66,15 @@ test_expect_success 'dcommit does the svn equivalent of an index merge' " git-svn dcommit " -test_expect_success 'commit another change from svn side' " - svn co $svnrepo t.svn && +test_expect_success 'commit another change from svn side' ' + svn co "$svnrepo" t.svn && cd t.svn && echo third line from svn >> file && poke file && - svn commit -m 'third line from svn' && + svn commit -m "third line from svn" && cd .. && rm -rf t.svn - " + ' test_expect_success 'multiple dcommit from git-svn will not clobber svn' " git reset --hard refs/remotes/git-svn && diff --git a/t/t9106-git-svn-dcommit-clobber-series.sh b/t/t9106-git-svn-dcommit-clobber-series.sh index ca8a00ed0a..a400dc7966 100755 --- a/t/t9106-git-svn-dcommit-clobber-series.sh +++ b/t/t9106-git-svn-dcommit-clobber-series.sh @@ -4,30 +4,30 @@ test_description='git-svn dcommit clobber series' . ./lib-git-svn.sh -test_expect_success 'initialize repo' " +test_expect_success 'initialize repo' ' mkdir import && cd import && - awk 'BEGIN { for (i = 1; i < 64; i++) { print i } }' > file - svn import -m 'initial' . $svnrepo && + awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file + svn import -m "initial" . "$svnrepo" && cd .. && - git svn init $svnrepo && + git svn init "$svnrepo" && git svn fetch && test -e file - " + ' -test_expect_success '(supposedly) non-conflicting change from SVN' " - test x\"\`sed -n -e 58p < file\`\" = x58 && - test x\"\`sed -n -e 61p < file\`\" = x61 && - svn co $svnrepo tmp && +test_expect_success '(supposedly) non-conflicting change from SVN' ' + test x"`sed -n -e 58p < file`" = x58 && + test x"`sed -n -e 61p < file`" = x61 && + svn co "$svnrepo" tmp && cd tmp && - perl -i -p -e 's/^58\$/5588/' file && - perl -i -p -e 's/^61\$/6611/' file && + perl -i -p -e "s/^58$/5588/" file && + perl -i -p -e "s/^61$/6611/" file && poke file && - test x\"\`sed -n -e 58p < file\`\" = x5588 && - test x\"\`sed -n -e 61p < file\`\" = x6611 && - svn commit -m '58 => 5588, 61 => 6611' && + test x"`sed -n -e 58p < file`" = x5588 && + test x"`sed -n -e 61p < file`" = x6611 && + svn commit -m "58 => 5588, 61 => 6611" && cd .. - " + ' test_expect_success 'some unrelated changes to git' " echo hi > life && diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh index 0a41d52c7a..d9b553ad55 100755 --- a/t/t9107-git-svn-migrate.sh +++ b/t/t9107-git-svn-migrate.sh @@ -3,61 +3,61 @@ test_description='git-svn metadata migrations from previous versions' . ./lib-git-svn.sh -test_expect_success 'setup old-looking metadata' " - cp $GIT_DIR/config $GIT_DIR/config-old-git-svn && +test_expect_success 'setup old-looking metadata' ' + cp "$GIT_DIR"/config "$GIT_DIR"/config-old-git-svn && mkdir import && cd import && for i in trunk branches/a branches/b \ tags/0.1 tags/0.2 tags/0.3; do - mkdir -p \$i && \ - echo hello >> \$i/README || exit 1 + mkdir -p $i && \ + echo hello >> $i/README || exit 1 done && \ - svn import -m test . $svnrepo + svn import -m test . "$svnrepo" cd .. && - git-svn init $svnrepo && + git-svn init "$svnrepo" && git-svn fetch && - mv $GIT_DIR/svn/* $GIT_DIR/ && - mv $GIT_DIR/svn/.metadata $GIT_DIR/ && - rmdir $GIT_DIR/svn && + mv "$GIT_DIR"/svn/* "$GIT_DIR"/ && + mv "$GIT_DIR"/svn/.metadata "$GIT_DIR"/ && + rmdir "$GIT_DIR"/svn && git update-ref refs/heads/git-svn-HEAD refs/remotes/git-svn && git update-ref refs/heads/svn-HEAD refs/remotes/git-svn && git update-ref -d refs/remotes/git-svn refs/remotes/git-svn - " + ' head=`git rev-parse --verify refs/heads/git-svn-HEAD^0` test_expect_success 'git-svn-HEAD is a real HEAD' "test -n '$head'" -test_expect_success 'initialize old-style (v0) git-svn layout' " - mkdir -p $GIT_DIR/git-svn/info $GIT_DIR/svn/info && - echo $svnrepo > $GIT_DIR/git-svn/info/url && - echo $svnrepo > $GIT_DIR/svn/info/url && +test_expect_success 'initialize old-style (v0) git-svn layout' ' + mkdir -p "$GIT_DIR"/git-svn/info "$GIT_DIR"/svn/info && + echo "$svnrepo" > "$GIT_DIR"/git-svn/info/url && + echo "$svnrepo" > "$GIT_DIR"/svn/info/url && git-svn migrate && - ! test -d $GIT_DIR/git-svn && + ! test -d "$GIT_DIR"/git-svn && git rev-parse --verify refs/remotes/git-svn^0 && git rev-parse --verify refs/remotes/svn^0 && - test \`git config --get svn-remote.svn.url\` = '$svnrepo' && - test \`git config --get svn-remote.svn.fetch\` = \ - ':refs/remotes/git-svn' - " + test "$(git config --get svn-remote.svn.url)" = "$svnrepo" && + test `git config --get svn-remote.svn.fetch` = \ + ":refs/remotes/git-svn" + ' -test_expect_success 'initialize a multi-repository repo' " - git-svn init $svnrepo -T trunk -t tags -b branches && +test_expect_success 'initialize a multi-repository repo' ' + git-svn init "$svnrepo" -T trunk -t tags -b branches && git config --get-all svn-remote.svn.fetch > fetch.out && - grep '^trunk:refs/remotes/trunk$' fetch.out && - test -n \"\`git config --get svn-remote.svn.branches \ - '^branches/\*:refs/remotes/\*$'\`\" && - test -n \"\`git config --get svn-remote.svn.tags \ - '^tags/\*:refs/remotes/tags/\*$'\`\" && + grep "^trunk:refs/remotes/trunk$" fetch.out && + test -n "`git config --get svn-remote.svn.branches \ + "^branches/\*:refs/remotes/\*$"`" && + test -n "`git config --get svn-remote.svn.tags \ + "^tags/\*:refs/remotes/tags/\*$"`" && git config --unset svn-remote.svn.branches \ - '^branches/\*:refs/remotes/\*$' && + "^branches/\*:refs/remotes/\*$" && git config --unset svn-remote.svn.tags \ - '^tags/\*:refs/remotes/tags/\*$' && - git config --add svn-remote.svn.fetch 'branches/a:refs/remotes/a' && - git config --add svn-remote.svn.fetch 'branches/b:refs/remotes/b' && + "^tags/\*:refs/remotes/tags/\*$" && + git config --add svn-remote.svn.fetch "branches/a:refs/remotes/a" && + git config --add svn-remote.svn.fetch "branches/b:refs/remotes/b" && for i in tags/0.1 tags/0.2 tags/0.3; do git config --add svn-remote.svn.fetch \ - \$i:refs/remotes/\$i || exit 1; done - " + $i:refs/remotes/$i || exit 1; done + ' # refs should all be different, but the trees should all be the same: test_expect_success 'multi-fetch works on partial urls + paths' " @@ -73,43 +73,43 @@ test_expect_success 'multi-fetch works on partial urls + paths' " refs/remotes/\$j\`\" ||exit 1; done; done " -test_expect_success 'migrate --minimize on old inited layout' " +test_expect_success 'migrate --minimize on old inited layout' ' git config --unset-all svn-remote.svn.fetch && git config --unset-all svn-remote.svn.url && - rm -rf $GIT_DIR/svn && - for i in \`cat fetch.out\`; do - path=\`expr \$i : '\\([^:]*\\):.*$'\` - ref=\`expr \$i : '[^:]*:refs/remotes/\\(.*\\)$'\` - if test -z \"\$ref\"; then continue; fi - if test -n \"\$path\"; then path=\"/\$path\"; fi - ( mkdir -p $GIT_DIR/svn/\$ref/info/ && - echo $svnrepo\$path > $GIT_DIR/svn/\$ref/info/url ) || exit 1; + rm -rf "$GIT_DIR"/svn && + for i in `cat fetch.out`; do + path=`expr $i : "\([^:]*\):.*$"` + ref=`expr $i : "[^:]*:refs/remotes/\(.*\)$"` + if test -z "$ref"; then continue; fi + if test -n "$path"; then path="/$path"; fi + ( mkdir -p "$GIT_DIR"/svn/$ref/info/ && + echo "$svnrepo"$path > "$GIT_DIR"/svn/$ref/info/url ) || exit 1; done && git-svn migrate --minimize && - test -z \"\`git config -l |grep -v '^svn-remote\.git-svn\.'\`\" && + test -z "`git config -l |grep -v "^svn-remote\.git-svn\."`" && git config --get-all svn-remote.svn.fetch > fetch.out && - grep '^trunk:refs/remotes/trunk$' fetch.out && - grep '^branches/a:refs/remotes/a$' fetch.out && - grep '^branches/b:refs/remotes/b$' fetch.out && - grep '^tags/0\.1:refs/remotes/tags/0\.1$' fetch.out && - grep '^tags/0\.2:refs/remotes/tags/0\.2$' fetch.out && - grep '^tags/0\.3:refs/remotes/tags/0\.3$' fetch.out - grep '^:refs/remotes/git-svn' fetch.out - " + grep "^trunk:refs/remotes/trunk$" fetch.out && + grep "^branches/a:refs/remotes/a$" fetch.out && + grep "^branches/b:refs/remotes/b$" fetch.out && + grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out && + grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out && + grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out + grep "^:refs/remotes/git-svn" fetch.out + ' -test_expect_success ".rev_db auto-converted to .rev_map.UUID" " +test_expect_success ".rev_db auto-converted to .rev_map.UUID" ' git-svn fetch -i trunk && - test -z \"\$(ls $GIT_DIR/svn/trunk/.rev_db.* 2>/dev/null)\" && - expect=\"\$(ls $GIT_DIR/svn/trunk/.rev_map.*)\" && - test -n \"\$expect\" && - rev_db=\$(echo \$expect | sed -e 's,_map,_db,') && - convert_to_rev_db \$expect \$rev_db && - rm -f \$expect && - test -f \$rev_db && + test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" && + expect="$(ls "$GIT_DIR"/svn/trunk/.rev_map.*)" && + test -n "$expect" && + rev_db="$(echo $expect | sed -e "s,_map,_db,")" && + convert_to_rev_db "$expect" "$rev_db" && + rm -f "$expect" && + test -f "$rev_db" && git-svn fetch -i trunk && - test -z \"\$(ls $GIT_DIR/svn/trunk/.rev_db.* 2>/dev/null)\" && - test ! -e $GIT_DIR/svn/trunk/.rev_db && - test -f \$expect - " + test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" && + test ! -e "$GIT_DIR"/svn/trunk/.rev_db && + test -f "$expect" + ' test_done diff --git a/t/t9108-git-svn-glob.sh b/t/t9108-git-svn-glob.sh index db4344cc84..f6f71d0545 100755 --- a/t/t9108-git-svn-glob.sh +++ b/t/t9108-git-svn-glob.sh @@ -10,77 +10,77 @@ start a new branch initial EOF -test_expect_success 'test refspec globbing' " +test_expect_success 'test refspec globbing' ' mkdir -p trunk/src/a trunk/src/b trunk/doc && - echo 'hello world' > trunk/src/a/readme && - echo 'goodbye world' > trunk/src/b/readme && - svn import -m 'initial' trunk $svnrepo/trunk && - svn co $svnrepo tmp && + echo "hello world" > trunk/src/a/readme && + echo "goodbye world" > trunk/src/b/readme && + svn import -m "initial" trunk "$svnrepo"/trunk && + svn co "$svnrepo" tmp && cd tmp && mkdir branches tags && svn add branches tags && svn cp trunk branches/start && - svn commit -m 'start a new branch' && + svn commit -m "start a new branch" && svn up && - echo 'hi' >> branches/start/src/b/readme && + echo "hi" >> branches/start/src/b/readme && poke branches/start/src/b/readme && - echo 'hey' >> branches/start/src/a/readme && + echo "hey" >> branches/start/src/a/readme && poke branches/start/src/a/readme && - svn commit -m 'hi' && + svn commit -m "hi" && svn up && svn cp branches/start tags/end && - echo 'bye' >> tags/end/src/b/readme && + echo "bye" >> tags/end/src/b/readme && poke tags/end/src/b/readme && - echo 'aye' >> tags/end/src/a/readme && + echo "aye" >> tags/end/src/a/readme && poke tags/end/src/a/readme && - svn commit -m 'the end' && - echo 'byebye' >> tags/end/src/b/readme && + svn commit -m "the end" && + echo "byebye" >> tags/end/src/b/readme && poke tags/end/src/b/readme && - svn commit -m 'nothing to see here' + svn commit -m "nothing to see here" cd .. && - git config --add svn-remote.svn.url $svnrepo && + git config --add svn-remote.svn.url "$svnrepo" && git config --add svn-remote.svn.fetch \ - 'trunk/src/a:refs/remotes/trunk' && + "trunk/src/a:refs/remotes/trunk" && git config --add svn-remote.svn.branches \ - 'branches/*/src/a:refs/remotes/branches/*' && + "branches/*/src/a:refs/remotes/branches/*" && git config --add svn-remote.svn.tags\ - 'tags/*/src/a:refs/remotes/tags/*' && + "tags/*/src/a:refs/remotes/tags/*" && git-svn multi-fetch && git log --pretty=oneline refs/remotes/tags/end | \ - sed -e 's/^.\{41\}//' > output.end && + sed -e "s/^.\{41\}//" > output.end && cmp expect.end output.end && - test \"\`git rev-parse refs/remotes/tags/end~1\`\" = \ - \"\`git rev-parse refs/remotes/branches/start\`\" && - test \"\`git rev-parse refs/remotes/branches/start~2\`\" = \ - \"\`git rev-parse refs/remotes/trunk\`\" - " + test "`git rev-parse refs/remotes/tags/end~1`" = \ + "`git rev-parse refs/remotes/branches/start`" && + test "`git rev-parse refs/remotes/branches/start~2`" = \ + "`git rev-parse refs/remotes/trunk`" + ' echo try to try > expect.two echo nothing to see here >> expect.two cat expect.end >> expect.two -test_expect_success 'test left-hand-side only globbing' " - git config --add svn-remote.two.url $svnrepo && +test_expect_success 'test left-hand-side only globbing' ' + git config --add svn-remote.two.url "$svnrepo" && git config --add svn-remote.two.fetch trunk:refs/remotes/two/trunk && git config --add svn-remote.two.branches \ - 'branches/*:refs/remotes/two/branches/*' && + "branches/*:refs/remotes/two/branches/*" && git config --add svn-remote.two.tags \ - 'tags/*:refs/remotes/two/tags/*' && + "tags/*:refs/remotes/two/tags/*" && cd tmp && - echo 'try try' >> tags/end/src/b/readme && + echo "try try" >> tags/end/src/b/readme && poke tags/end/src/b/readme && - svn commit -m 'try to try' + svn commit -m "try to try" cd .. && git-svn fetch two && - test \`git rev-list refs/remotes/two/tags/end | wc -l\` -eq 6 && - test \`git rev-list refs/remotes/two/branches/start | wc -l\` -eq 3 && - test \`git rev-parse refs/remotes/two/branches/start~2\` = \ - \`git rev-parse refs/remotes/two/trunk\` && - test \`git rev-parse refs/remotes/two/tags/end~3\` = \ - \`git rev-parse refs/remotes/two/branches/start\` && + test `git rev-list refs/remotes/two/tags/end | wc -l` -eq 6 && + test `git rev-list refs/remotes/two/branches/start | wc -l` -eq 3 && + test `git rev-parse refs/remotes/two/branches/start~2` = \ + `git rev-parse refs/remotes/two/trunk` && + test `git rev-parse refs/remotes/two/tags/end~3` = \ + `git rev-parse refs/remotes/two/branches/start` && git log --pretty=oneline refs/remotes/two/tags/end | \ - sed -e 's/^.\{41\}//' > output.two && + sed -e "s/^.\{41\}//" > output.two && cmp expect.two output.two - " + ' test_done diff --git a/t/t9110-git-svn-use-svm-props.sh b/t/t9110-git-svn-use-svm-props.sh index 6235af4db8..047659fde1 100755 --- a/t/t9110-git-svn-use-svm-props.sh +++ b/t/t9110-git-svn-use-svm-props.sh @@ -7,15 +7,15 @@ test_description='git-svn useSvmProps test' . ./lib-git-svn.sh -test_expect_success 'load svm repo' " - svnadmin load -q $rawsvnrepo < ../t9110/svm.dump && - git-svn init --minimize-url -R arr -i bar $svnrepo/mirror/arr && - git-svn init --minimize-url -R argh -i dir $svnrepo/mirror/argh && +test_expect_success 'load svm repo' ' + svnadmin load -q "$rawsvnrepo" < ../t9110/svm.dump && + git-svn init --minimize-url -R arr -i bar "$svnrepo"/mirror/arr && + git-svn init --minimize-url -R argh -i dir "$svnrepo"/mirror/argh && git-svn init --minimize-url -R argh -i e \ - $svnrepo/mirror/argh/a/b/c/d/e && + "$svnrepo"/mirror/argh/a/b/c/d/e && git config svn.useSvmProps true && git-svn fetch --all - " + ' uuid=161ce429-a9dd-4828-af4a-52023f968c89 diff --git a/t/t9111-git-svn-use-svnsync-props.sh b/t/t9111-git-svn-use-svnsync-props.sh index ec7dedd48b..a8d74dcd3a 100755 --- a/t/t9111-git-svn-use-svnsync-props.sh +++ b/t/t9111-git-svn-use-svnsync-props.sh @@ -7,14 +7,14 @@ test_description='git-svn useSvnsyncProps test' . ./lib-git-svn.sh -test_expect_success 'load svnsync repo' " - svnadmin load -q $rawsvnrepo < ../t9111/svnsync.dump && - git-svn init --minimize-url -R arr -i bar $svnrepo/bar && - git-svn init --minimize-url -R argh -i dir $svnrepo/dir && - git-svn init --minimize-url -R argh -i e $svnrepo/dir/a/b/c/d/e && +test_expect_success 'load svnsync repo' ' + svnadmin load -q "$rawsvnrepo" < ../t9111/svnsync.dump && + git-svn init --minimize-url -R arr -i bar "$svnrepo"/bar && + git-svn init --minimize-url -R argh -i dir "$svnrepo"/dir && + git-svn init --minimize-url -R argh -i e "$svnrepo"/dir/a/b/c/d/e && git config svn.useSvnsyncProps true && git-svn fetch --all - " + ' uuid=161ce429-a9dd-4828-af4a-52023f968c89 diff --git a/t/t9112-git-svn-md5less-file.sh b/t/t9112-git-svn-md5less-file.sh index 646a5f0cdb..d470a920e4 100755 --- a/t/t9112-git-svn-md5less-file.sh +++ b/t/t9112-git-svn-md5less-file.sh @@ -40,8 +40,8 @@ PROPS-END EOF -test_expect_success 'load svn dumpfile' "svnadmin load $rawsvnrepo < dumpfile.svn" +test_expect_success 'load svn dumpfile' 'svnadmin load "$rawsvnrepo" < dumpfile.svn' -test_expect_success 'initialize git-svn' "git-svn init $svnrepo" +test_expect_success 'initialize git-svn' 'git-svn init "$svnrepo"' test_expect_success 'fetch revisions from svn' 'git-svn fetch' test_done diff --git a/t/t9113-git-svn-dcommit-new-file.sh b/t/t9113-git-svn-dcommit-new-file.sh index 9ef0db9044..31c929b573 100755 --- a/t/t9113-git-svn-dcommit-new-file.sh +++ b/t/t9113-git-svn-dcommit-new-file.sh @@ -15,18 +15,18 @@ test_description='git-svn dcommit new files over svn:// test' start_svnserve () { svnserve --listen-port $SVNSERVE_PORT \ - --root $rawsvnrepo \ + --root "$rawsvnrepo" \ --listen-once \ --listen-host 127.0.0.1 & } -test_expect_success 'start tracking an empty repo' " - svn mkdir -m 'empty dir' $svnrepo/empty-dir && - echo anon-access = write >> $rawsvnrepo/conf/svnserve.conf && +test_expect_success 'start tracking an empty repo' ' + svn mkdir -m "empty dir" "$svnrepo"/empty-dir && + echo anon-access = write >> "$rawsvnrepo"/conf/svnserve.conf && start_svnserve && git svn init svn://127.0.0.1:$SVNSERVE_PORT && git svn fetch - " + ' test_expect_success 'create files in new directory with dcommit' " mkdir git-new-dir && diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh index 225060b88b..61d7781616 100755 --- a/t/t9114-git-svn-dcommit-merge.sh +++ b/t/t9114-git-svn-dcommit-merge.sh @@ -34,35 +34,35 @@ cat << EOF EOF } -test_expect_success 'setup svn repository' " - svn co $svnrepo mysvnwork && +test_expect_success 'setup svn repository' ' + svn co "$svnrepo" mysvnwork && mkdir -p mysvnwork/trunk && cd mysvnwork && big_text_block >> trunk/README && svn add trunk && - svn ci -m 'first commit' trunk && + svn ci -m "first commit" trunk && cd .. - " + ' -test_expect_success 'setup git mirror and merge' " - git svn init $svnrepo -t tags -T trunk -b branches && +test_expect_success 'setup git mirror and merge' ' + git svn init "$svnrepo" -t tags -T trunk -b branches && git svn fetch && git checkout --track -b svn remotes/trunk && git checkout -b merge && echo new file > new_file && git add new_file && - git commit -a -m 'New file' && + git commit -a -m "New file" && echo hello >> README && - git commit -a -m 'hello' && + git commit -a -m "hello" && echo add some stuff >> new_file && - git commit -a -m 'add some stuff' && + git commit -a -m "add some stuff" && git checkout svn && mv -f README tmp && echo friend > README && cat tmp >> README && - git commit -a -m 'friend' && + git commit -a -m "friend" && git pull . merge - " + ' test_debug 'gitk --all & sleep 1' diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh index 182299cbb5..f0fbd3aff7 100755 --- a/t/t9115-git-svn-dcommit-funky-renames.sh +++ b/t/t9115-git-svn-dcommit-funky-renames.sh @@ -7,16 +7,16 @@ test_description='git-svn dcommit can commit renames of files with ugly names' . ./lib-git-svn.sh -test_expect_success 'load repository with strange names' " - svnadmin load -q $rawsvnrepo < ../t9115/funky-names.dump && - start_httpd - " +test_expect_success 'load repository with strange names' ' + svnadmin load -q "$rawsvnrepo" < ../t9115/funky-names.dump && + start_httpd gtk+ + ' -test_expect_success 'init and fetch repository' " - git svn init $svnrepo && +test_expect_success 'init and fetch repository' ' + git svn init "$svnrepo" && git svn fetch && git reset --hard git-svn - " + ' test_expect_success 'create file in existing ugly and empty dir' ' mkdir "#{bad_directory_name}" && @@ -49,6 +49,39 @@ test_expect_success 'rename pretty file into ugly one' ' git svn dcommit ' +test_expect_success 'add a file with plus signs' ' + echo .. > +_+ && + git update-index --add +_+ && + git commit -m plus && + mkdir gtk+ && + git mv +_+ gtk+/_+_ && + git commit -m plus_dir && + git svn dcommit + ' + +test_expect_success 'clone the repository to test rebase' ' + git svn clone "$svnrepo" test-rebase && + cd test-rebase && + echo test-rebase > test-rebase && + git add test-rebase && + git commit -m test-rebase && + cd .. + ' + +test_expect_success 'make a commit to test rebase' ' + echo test-rebase-main > test-rebase-main && + git add test-rebase-main && + git commit -m test-rebase-main && + git svn dcommit + ' + +test_expect_success 'git-svn rebase works inside a fresh-cloned repository' ' + cd test-rebase && + git svn rebase && + test -e test-rebase-main && + test -e test-rebase + ' + stop_httpd test_done diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh index e1e8bdf0e3..4b2cc878f6 100755 --- a/t/t9116-git-svn-log.sh +++ b/t/t9116-git-svn-log.sh @@ -6,17 +6,17 @@ test_description='git-svn log tests' . ./lib-git-svn.sh -test_expect_success 'setup repository and import' " +test_expect_success 'setup repository and import' ' mkdir import && cd import && for i in trunk branches/a branches/b \ tags/0.1 tags/0.2 tags/0.3; do - mkdir -p \$i && \ - echo hello >> \$i/README || exit 1 + mkdir -p $i && \ + echo hello >> $i/README || exit 1 done && \ - svn import -m test . $svnrepo + svn import -m test . "$svnrepo" cd .. && - git-svn init $svnrepo -T trunk -b branches -t tags && + git-svn init "$svnrepo" -T trunk -b branches -t tags && git-svn fetch && git reset --hard trunk && echo bye >> README && @@ -37,7 +37,7 @@ test_expect_success 'setup repository and import' " echo try >> README && git commit -a -m try && git svn dcommit - " + ' test_expect_success 'run log' " git reset --hard a && diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh index d482b407f2..7a689bb1cd 100755 --- a/t/t9117-git-svn-init-clone.sh +++ b/t/t9117-git-svn-init-clone.sh @@ -13,43 +13,43 @@ rm -r .git mkdir tmp cd tmp -test_expect_success 'setup svnrepo' " +test_expect_success 'setup svnrepo' ' mkdir project project/trunk project/branches project/tags && echo foo > project/trunk/foo && - svn import -m '$test_description' project $svnrepo/project && + svn import -m "$test_description" project "$svnrepo"/project && rm -rf project - " + ' -test_expect_success 'basic clone' " +test_expect_success 'basic clone' ' test ! -d trunk && - git svn clone $svnrepo/project/trunk && + git svn clone "$svnrepo"/project/trunk && test -d trunk/.git/svn && test -e trunk/foo && rm -rf trunk - " + ' -test_expect_success 'clone to target directory' " +test_expect_success 'clone to target directory' ' test ! -d target && - git svn clone $svnrepo/project/trunk target && + git svn clone "$svnrepo"/project/trunk target && test -d target/.git/svn && test -e target/foo && rm -rf target - " + ' -test_expect_success 'clone with --stdlayout' " +test_expect_success 'clone with --stdlayout' ' test ! -d project && - git svn clone -s $svnrepo/project && + git svn clone -s "$svnrepo"/project && test -d project/.git/svn && test -e project/foo && rm -rf project - " + ' -test_expect_success 'clone to target directory with --stdlayout' " +test_expect_success 'clone to target directory with --stdlayout' ' test ! -d target && - git svn clone -s $svnrepo/project target && + git svn clone -s "$svnrepo"/project target && test -d target/.git/svn && test -e target/foo && rm -rf target - " + ' test_done diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh index 640bb066f3..3281cbd347 100755 --- a/t/t9118-git-svn-funky-branch-names.sh +++ b/t/t9118-git-svn-funky-branch-names.sh @@ -6,25 +6,25 @@ test_description='git-svn funky branch names' . ./lib-git-svn.sh -test_expect_success 'setup svnrepo' " +test_expect_success 'setup svnrepo' ' mkdir project project/trunk project/branches project/tags && echo foo > project/trunk/foo && - svn import -m '$test_description' project \"$svnrepo/pr ject\" && + svn import -m "$test_description" project "$svnrepo/pr ject" && rm -rf project && - svn cp -m 'fun' \"$svnrepo/pr ject/trunk\" \ - \"$svnrepo/pr ject/branches/fun plugin\" && - svn cp -m 'more fun!' \"$svnrepo/pr ject/branches/fun plugin\" \ - \"$svnrepo/pr ject/branches/more fun plugin!\" && + svn cp -m "fun" "$svnrepo/pr ject/trunk" \ + "$svnrepo/pr ject/branches/fun plugin" && + svn cp -m "more fun!" "$svnrepo/pr ject/branches/fun plugin" \ + "$svnrepo/pr ject/branches/more fun plugin!" && start_httpd - " + ' -test_expect_success 'test clone with funky branch names' " - git svn clone -s \"$svnrepo/pr ject\" project && +test_expect_success 'test clone with funky branch names' ' + git svn clone -s "$svnrepo/pr ject" project && cd project && - git rev-parse 'refs/remotes/fun%20plugin' && - git rev-parse 'refs/remotes/more%20fun%20plugin!' && + git rev-parse "refs/remotes/fun%20plugin" && + git rev-parse "refs/remotes/more%20fun%20plugin!" && cd .. - " + ' test_expect_success 'test dcommit to funky branch' " cd project && diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh index 9a4eabe523..5979e133b9 100755 --- a/t/t9120-git-svn-clone-with-percent-escapes.sh +++ b/t/t9120-git-svn-clone-with-percent-escapes.sh @@ -6,13 +6,13 @@ test_description='git-svn clone with percent escapes' . ./lib-git-svn.sh -test_expect_success 'setup svnrepo' " +test_expect_success 'setup svnrepo' ' mkdir project project/trunk project/branches project/tags && echo foo > project/trunk/foo && - svn import -m '$test_description' project '$svnrepo/pr ject' && + svn import -m "$test_description" project "$svnrepo/pr ject" && rm -rf project && start_httpd -" +' if test "$SVN_HTTPD_PORT" = "" then diff --git a/t/t9121-git-svn-fetch-renamed-dir.sh b/t/t9121-git-svn-fetch-renamed-dir.sh index 5143ed6066..99230b0810 100755 --- a/t/t9121-git-svn-fetch-renamed-dir.sh +++ b/t/t9121-git-svn-fetch-renamed-dir.sh @@ -7,14 +7,14 @@ test_description='git-svn can fetch renamed directories' . ./lib-git-svn.sh -test_expect_success 'load repository with renamed directory' " - svnadmin load -q $rawsvnrepo < ../t9121/renamed-dir.dump - " +test_expect_success 'load repository with renamed directory' ' + svnadmin load -q "$rawsvnrepo" < ../t9121/renamed-dir.dump + ' -test_expect_success 'init and fetch repository' " - git svn init $svnrepo/newname && +test_expect_success 'init and fetch repository' ' + git svn init "$svnrepo/newname" && git svn fetch - " + ' test_done diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index 166b43f783..e97aaa6c2a 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -153,21 +153,21 @@ test_expect_success 'req_Root failure (conflicting roots)' \ tail log | grep "^error 1 Conflicting roots specified$"' test_expect_success 'req_Root (strict paths)' \ - 'cat request-anonymous | git-cvsserver --strict-paths pserver $SERVERDIR >log 2>&1 && + 'cat request-anonymous | git-cvsserver --strict-paths pserver "$SERVERDIR" >log 2>&1 && sed -ne \$p log | grep "^I LOVE YOU$"' test_expect_success 'req_Root failure (strict-paths)' ' ! cat request-anonymous | - git-cvsserver --strict-paths pserver $WORKDIR >log 2>&1 + git-cvsserver --strict-paths pserver "$WORKDIR" >log 2>&1 ' test_expect_success 'req_Root (w/o strict-paths)' \ - 'cat request-anonymous | git-cvsserver pserver $WORKDIR/ >log 2>&1 && + 'cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 && sed -ne \$p log | grep "^I LOVE YOU$"' test_expect_success 'req_Root failure (w/o strict-paths)' ' ! cat request-anonymous | - git-cvsserver pserver $WORKDIR/gitcvs >log 2>&1 + git-cvsserver pserver "$WORKDIR/gitcvs" >log 2>&1 ' cat >request-base <<EOF @@ -180,25 +180,25 @@ Root /gitcvs.git EOF test_expect_success 'req_Root (base-path)' \ - 'cat request-base | git-cvsserver --strict-paths --base-path $WORKDIR/ pserver $SERVERDIR >log 2>&1 && + 'cat request-base | git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 && sed -ne \$p log | grep "^I LOVE YOU$"' test_expect_success 'req_Root failure (base-path)' ' ! cat request-anonymous | - git-cvsserver --strict-paths --base-path $WORKDIR pserver $SERVERDIR >log 2>&1 + git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" >log 2>&1 ' GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1 test_expect_success 'req_Root (export-all)' \ - 'cat request-anonymous | git-cvsserver --export-all pserver $WORKDIR >log 2>&1 && + 'cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 && sed -ne \$p log | grep "^I LOVE YOU$"' test_expect_success 'req_Root failure (export-all w/o whitelist)' \ '! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)' test_expect_success 'req_Root (everything together)' \ - 'cat request-base | git-cvsserver --export-all --strict-paths --base-path $WORKDIR/ pserver $SERVERDIR >log 2>&1 && + 'cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 && sed -ne \$p log | grep "^I LOVE YOU$"' GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true || exit 1 diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 061a2596d3..ae7082be1d 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -10,6 +10,7 @@ commandline, and checks that it would not write any errors or warnings to log.' gitweb_init () { + safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')" cat >gitweb_config.perl <<EOF #!/usr/bin/perl @@ -17,16 +18,16 @@ gitweb_init () { our \$version = "current"; our \$GIT = "git"; -our \$projectroot = "$(pwd)"; +our \$projectroot = "$safe_pwd"; our \$project_maxdepth = 8; our \$home_link_str = "projects"; our \$site_name = "[localhost]"; our \$site_header = ""; our \$site_footer = ""; our \$home_text = "indextext.html"; -our @stylesheets = ("file:///$(pwd)/../../gitweb/gitweb.css"); -our \$logo = "file:///$(pwd)/../../gitweb/git-logo.png"; -our \$favicon = "file:///$(pwd)/../../gitweb/git-favicon.png"; +our @stylesheets = ("file:///$safe_pwd/../../gitweb/gitweb.css"); +our \$logo = "file:///$safe_pwd/../../gitweb/git-logo.png"; +our \$favicon = "file:///$safe_pwd/../../gitweb/git-favicon.png"; our \$projects_list = ""; our \$export_ok = ""; our \$strict_export = ""; @@ -39,19 +40,21 @@ EOF } gitweb_run () { - export GATEWAY_INTERFACE="CGI/1.1" - export HTTP_ACCEPT="*/*" - export REQUEST_METHOD="GET" - export QUERY_STRING=""$1"" - export PATH_INFO=""$2"" + GATEWAY_INTERFACE="CGI/1.1" + HTTP_ACCEPT="*/*" + REQUEST_METHOD="GET" + QUERY_STRING=""$1"" + PATH_INFO=""$2"" + export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD QUERY_STRING PATH_INFO - export GITWEB_CONFIG=$(pwd)/gitweb_config.perl + GITWEB_CONFIG=$(pwd)/gitweb_config.perl + export GITWEB_CONFIG # some of git commands write to STDERR on error, but this is not # written to web server logs, so we are not interested in that: # we are interested only in properly formatted errors/warnings rm -f gitweb.log && - perl -- $(pwd)/../../gitweb/gitweb.perl \ + perl -- "$(pwd)/../../gitweb/gitweb.perl" \ >/dev/null 2>gitweb.log && if grep -q -s "^[[]" gitweb.log >/dev/null; then false; else true; fi diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh index 00a74ee738..0b115a17ab 100755 --- a/t/t9600-cvsimport.sh +++ b/t/t9600-cvsimport.sh @@ -36,7 +36,7 @@ test_expect_success 'setup cvsroot' 'cvs init' test_expect_success 'setup a cvs module' ' - mkdir $CVSROOT/module && + mkdir "$CVSROOT/module" && cvs co -d module-cvs module && cd module-cvs && cat <<EOF >o_fortuna && diff --git a/t/test-lib.sh b/t/test-lib.sh index 7c2a8ba77d..5002fb04b5 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -160,6 +160,22 @@ die () { trap 'die' exit +# The semantics of the editor variables are that of invoking +# sh -c "$EDITOR \"$@\"" files ... +# +# If our trash directory contains shell metacharacters, they will be +# interpreted if we just set $EDITOR directly, so do a little dance with +# environment variables to work around this. +# +# In particular, quoting isn't enough, as the path may contain the same quote +# that we're using. +test_set_editor () { + FAKE_EDITOR="$1" + export FAKE_EDITOR + VISUAL='"$FAKE_EDITOR"' + export VISUAL +} + test_tick () { if test -z "${test_tick+set}" then @@ -329,7 +345,7 @@ test_create_repo () { repo="$1" mkdir "$repo" cd "$repo" || error "Cannot setup test environment" - "$GIT_EXEC_PATH/git" init --template=$GIT_EXEC_PATH/templates/blt/ >/dev/null 2>&1 || + "$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >/dev/null 2>&1 || error "cannot run git init -- have you built things yet?" mv .git/hooks .git/hooks-disabled cd "$owd" @@ -395,14 +411,14 @@ fi . ../GIT-BUILD-OPTIONS # Test repository -test=trash +test="trash directory" rm -fr "$test" || { trap - exit echo >&5 "FATAL: Cannot prepare test area" exit 1 } -test_create_repo $test +test_create_repo "$test" cd "$test" this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$') diff --git a/transport.c b/transport.c index 393e0e8fe2..1bc16f2b65 100644 --- a/transport.c +++ b/transport.c @@ -441,10 +441,14 @@ static struct ref *get_refs_via_curl(struct transport *transport) struct ref *ref = NULL; struct ref *last_ref = NULL; + struct walker *walker; + if (!transport->data) transport->data = get_http_walker(transport->url, transport->remote); + walker = transport->data; + refs_url = xmalloc(strlen(transport->url) + 11); sprintf(refs_url, "%s/info/refs", transport->url); @@ -500,6 +504,15 @@ static struct ref *get_refs_via_curl(struct transport *transport) strbuf_release(&buffer); + ref = alloc_ref_from_str("HEAD"); + if (!walker->fetch_ref(walker, ref) && + !resolve_remote_symref(ref, refs)) { + ref->next = refs; + refs = ref; + } else { + free(ref); + } + return refs; } @@ -532,9 +545,8 @@ static struct ref *get_refs_from_bundle(struct transport *transport) die ("Could not read bundle '%s'.", transport->url); for (i = 0; i < data->header.references.nr; i++) { struct ref_list_entry *e = data->header.references.list + i; - struct ref *ref = alloc_ref(strlen(e->name) + 1); + struct ref *ref = alloc_ref_from_str(e->name); hashcpy(ref->old_sha1, e->sha1); - strcpy(ref->name, e->name); ref->next = result; result = ref; } diff --git a/unpack-trees.c b/unpack-trees.c index a59f47557a..1ab28fda45 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -26,11 +26,12 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce, * directories, in case this unlink is the removal of the * last entry in the directory -- empty directories are removed. */ -static void unlink_entry(char *name, char *last_symlink) +static void unlink_entry(struct cache_entry *ce) { char *cp, *prev; + char *name = ce->name; - if (has_symlink_leading_path(name, last_symlink)) + if (has_symlink_leading_path(ce_namelen(ce), ce->name)) return; if (unlink(name)) return; @@ -58,7 +59,6 @@ static int check_updates(struct unpack_trees_options *o) { unsigned cnt = 0, total = 0; struct progress *progress = NULL; - char last_symlink[PATH_MAX]; struct index_state *index = &o->result; int i; int errs = 0; @@ -75,24 +75,27 @@ static int check_updates(struct unpack_trees_options *o) cnt = 0; } - *last_symlink = '\0'; for (i = 0; i < index->cache_nr; i++) { struct cache_entry *ce = index->cache[i]; - if (ce->ce_flags & (CE_UPDATE | CE_REMOVE)) - display_progress(progress, ++cnt); if (ce->ce_flags & CE_REMOVE) { + display_progress(progress, ++cnt); if (o->update) - unlink_entry(ce->name, last_symlink); + unlink_entry(ce); remove_index_entry_at(&o->result, i); i--; continue; } + } + + for (i = 0; i < index->cache_nr; i++) { + struct cache_entry *ce = index->cache[i]; + if (ce->ce_flags & CE_UPDATE) { + display_progress(progress, ++cnt); ce->ce_flags &= ~CE_UPDATE; if (o->update) { errs |= checkout_entry(ce, &state, NULL); - *last_symlink = '\0'; } } } @@ -521,6 +524,22 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action, } /* + * This gets called when there was no index entry for the tree entry 'dst', + * but we found a file in the working tree that 'lstat()' said was fine, + * and we're on a case-insensitive filesystem. + * + * See if we can find a case-insensitive match in the index that also + * matches the stat information, and assume it's that other file! + */ +static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst, struct stat *st) +{ + struct cache_entry *src; + + src = index_name_exists(o->src_index, dst->name, ce_namelen(dst), 1); + return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID); +} + +/* * We do not want to remove or overwrite a working tree file that * is not tracked, unless it is ignored. */ @@ -532,12 +551,23 @@ static int verify_absent(struct cache_entry *ce, const char *action, if (o->index_only || o->reset || !o->update) return 0; - if (has_symlink_leading_path(ce->name, NULL)) + if (has_symlink_leading_path(ce_namelen(ce), ce->name)) return 0; if (!lstat(ce->name, &st)) { int cnt; int dtype = ce_to_dtype(ce); + struct cache_entry *result; + + /* + * It may be that the 'lstat()' succeeded even though + * target 'ce' was absent, because there is an old + * entry that is different only in case.. + * + * Ignore that lstat() if it matches. + */ + if (ignore_case && icase_exists(o, ce, &st)) + return 0; if (o->dir && excluded(o->dir, ce->name, &dtype)) /* @@ -581,10 +611,9 @@ static int verify_absent(struct cache_entry *ce, const char *action, * delete this path, which is in a subdirectory that * is being replaced with a blob. */ - cnt = index_name_pos(&o->result, ce->name, strlen(ce->name)); - if (0 <= cnt) { - struct cache_entry *ce = o->result.cache[cnt]; - if (ce->ce_flags & CE_REMOVE) + result = index_name_exists(&o->result, ce->name, ce_namelen(ce), 0); + if (result) { + if (result->ce_flags & CE_REMOVE) return 0; } diff --git a/unpack-trees.h b/unpack-trees.h index 50453ed20f..d436d6ced9 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -9,16 +9,16 @@ typedef int (*merge_fn_t)(struct cache_entry **src, struct unpack_trees_options *options); struct unpack_trees_options { - int reset; - int merge; - int update; - int index_only; - int nontrivial_merge; - int trivial_merges_only; - int verbose_update; - int aggressive; - int skip_unmerged; - int gently; + unsigned int reset:1, + merge:1, + update:1, + index_only:1, + nontrivial_merge:1, + trivial_merges_only:1, + verbose_update:1, + aggressive:1, + skip_unmerged:1, + gently:1; const char *prefix; int pos; struct dir_struct *dir; @@ -31,7 +31,7 @@ struct unpack_trees_options { void *unpack_data; struct index_state *dst_index; - const struct index_state *src_index; + struct index_state *src_index; struct index_state result; }; @@ -190,9 +190,13 @@ static int interpret_target(struct walker *walker, char *target, unsigned char * if (!get_sha1_hex(target, sha1)) return 0; if (!check_ref_format(target)) { - if (!walker->fetch_ref(walker, target, sha1)) { + struct ref *ref = alloc_ref_from_str(target); + if (!walker->fetch_ref(walker, ref)) { + hashcpy(sha1, ref->old_sha1); + free(ref); return 0; } + free(ref); } return -1; } @@ -5,7 +5,7 @@ struct walker { void *data; - int (*fetch_ref)(struct walker *, char *ref, unsigned char *sha1); + int (*fetch_ref)(struct walker *, struct ref *ref); void (*prefetch)(struct walker *, unsigned char *sha1); int (*fetch)(struct walker *, unsigned char *sha1); void (*cleanup)(struct walker *); diff --git a/wt-status.c b/wt-status.c index 532b4ea2c1..a44c543375 100644 --- a/wt-status.c +++ b/wt-status.c @@ -206,7 +206,7 @@ static void wt_status_print_updated(struct wt_status *s) rev.diffopt.format_callback = wt_status_print_updated_cb; rev.diffopt.format_callback_data = s; rev.diffopt.detect_rename = 1; - rev.diffopt.rename_limit = 100; + rev.diffopt.rename_limit = 200; rev.diffopt.break_opt = 0; run_diff_index(&rev, 1); } diff --git a/wt-status.h b/wt-status.h index 02afaa60ee..7d61410b17 100644 --- a/wt-status.h +++ b/wt-status.h @@ -28,8 +28,8 @@ struct wt_status { }; int git_status_config(const char *var, const char *value); -int wt_status_use_color; -int wt_status_relative_paths; +extern int wt_status_use_color; +extern int wt_status_relative_paths; void wt_status_prepare(struct wt_status *s); void wt_status_print(struct wt_status *s); |