diff options
139 files changed, 3018 insertions, 853 deletions
diff --git a/Documentation/RelNotes-1.5.2.2.txt b/Documentation/RelNotes-1.5.2.2.txt index f6393f8a94..7bfa341750 100644 --- a/Documentation/RelNotes-1.5.2.2.txt +++ b/Documentation/RelNotes-1.5.2.2.txt @@ -45,7 +45,7 @@ Fixes since v1.5.2.1 correctly when the branch name had slash in it. - The email address of the user specified with user.email - configuration was overriden by EMAIL environment variable. + configuration was overridden by EMAIL environment variable. - The tree parser did not warn about tree entries with nonsense file modes, and assumed they must be blobs. diff --git a/Documentation/RelNotes-1.6.0.2.txt b/Documentation/RelNotes-1.6.0.2.txt index 7a9646fc4f..51b32f5d94 100644 --- a/Documentation/RelNotes-1.6.0.2.txt +++ b/Documentation/RelNotes-1.6.0.2.txt @@ -7,7 +7,7 @@ Fixes since v1.6.0.1 * Installation on platforms that needs .exe suffix to git-* programs were broken in 1.6.0.1. -* Installation on filesystems without symbolic links support did nto +* Installation on filesystems without symbolic links support did not work well. * In-tree documentations and test scripts now use "git foo" form to set a diff --git a/Documentation/RelNotes-1.6.1.1.txt b/Documentation/RelNotes-1.6.1.1.txt index 88454c1973..8c594ba02f 100644 --- a/Documentation/RelNotes-1.6.1.1.txt +++ b/Documentation/RelNotes-1.6.1.1.txt @@ -41,11 +41,11 @@ Fixes since v1.6.1 work tree upon delete/modify conflict. * "git merge -s recursive" didn't leave the index unmerged for entries with - rename/delete conflictd. + rename/delete conflicts. * "git merge -s recursive" clobbered untracked files in the work tree. -* "git mv -k" with more than one errorneous paths misbehaved. +* "git mv -k" with more than one erroneous paths misbehaved. * "git read-tree -m -u" hence branch switching incorrectly lost a subdirectory in rare cases. diff --git a/Documentation/RelNotes-1.6.1.2.txt b/Documentation/RelNotes-1.6.1.2.txt index 230aa3d8e8..be37cbb858 100644 --- a/Documentation/RelNotes-1.6.1.2.txt +++ b/Documentation/RelNotes-1.6.1.2.txt @@ -4,8 +4,8 @@ GIT v1.6.1.2 Release Notes Fixes since v1.6.1.1 -------------------- -* The logic for rename detectin in internal diff used by commands like - "git diff" and "git blame" have been optimized to avoid loading the same +* The logic for rename detection in internal diff used by commands like + "git diff" and "git blame" has been optimized to avoid loading the same blob repeatedly. * We did not allow writing out a blob that is larger than 2GB for no good diff --git a/Documentation/RelNotes-1.6.2.1.txt b/Documentation/RelNotes-1.6.2.1.txt new file mode 100644 index 0000000000..3a2d3bf849 --- /dev/null +++ b/Documentation/RelNotes-1.6.2.1.txt @@ -0,0 +1,6 @@ +GIT v1.6.2.1 Release Notes +========================== + +Fixes since v1.6.2 +------------------ + diff --git a/Documentation/RelNotes-1.6.2.txt b/Documentation/RelNotes-1.6.2.txt index d3e9583abf..ad060f4f89 100644 --- a/Documentation/RelNotes-1.6.2.txt +++ b/Documentation/RelNotes-1.6.2.txt @@ -150,6 +150,9 @@ v1.6.1.X series. * "git filter-branch" incorrectly tried to update a nonexistent work tree at the end when it is run in a bare repository. +* "git gc" did not work if your repository was created with an ancient git + and never had any pack files in it before. + * "git mergetool" used to ignore autocrlf and other attributes based content rewriting. @@ -159,9 +162,3 @@ v1.6.1.X series. * "git -p cmd" when cmd is not a built-in one left the display in funny state when killed in the middle. - --- -exec >/var/tmp/1 -v1.6.2-rc0-89-gf7a2bdb -echo O=$(git describe master) -git shortlog --no-merges $O..master ^maint diff --git a/Documentation/RelNotes-1.6.3.txt b/Documentation/RelNotes-1.6.3.txt new file mode 100644 index 0000000000..ee1fddb76a --- /dev/null +++ b/Documentation/RelNotes-1.6.3.txt @@ -0,0 +1,104 @@ +GIT v1.6.3 Release Notes +======================== + +With the next major release, "git push" into a branch that is +currently checked out will be refused by default. You can choose +what should happen upon such a push by setting the configuration +variable receive.denyCurrentBranch in the receiving repository. + +To ease the transition plan, the receiving repository of such a +push running this release will issue a big warning when the +configuration variable is missing. Please refer to: + + http://git.or.cz/gitwiki/GitFaq#non-bare + http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007 + +for more details on the reason why this change is needed and the +transition plan. + +For a similar reason, "git push $there :$killed" to delete the branch +$killed in a remote repository $there, if $killed branch is the current +branch pointed at by its HEAD, gets a large warning. You can choose what +should happen upon such a push by setting the configuration variable +receive.denyDeleteCurrent in the receiving repository. + + +Updates since v1.6.2 +-------------------- + +(subsystems) + +(performance) + +(usability, bells and whistles) + +* "--pretty=<style>" option to the log family of commands can now be + spelled as "--format=<style>". In addition, --format=%formatstring + is a short-hand for --pretty=tformat:%formatstring. + +* "--oneline" is a synonym for "--pretty=oneline --abbrev=commit". + +* If you realize that you botched the patch when you are editing hunks + with the 'edit' action in git-add -i/-p, you can abort the editor to + tell git not to apply it. + +* git-archive learned --output=<file> option. + +* git-bisect shows not just the number of remaining commits whose goodness + is unknown, but also shows the estimated number of remaining rounds. + +* git-branch -r shows HEAD symref that points at a remote branch in + interest of each tracked remote repository. + +* git-config learned -e option to open an editor to edit the config file + directly. + +* git-format-patch can be told to use attachment with a new configuration, + format.attach. + +* git-imap-send learned to work around Thunderbird's inability to easily + disable format=flowed with a new configuration, imap.preformattedHTML. + +* git-rebase can be told to rebase the series even if your branch is a + descendant of the commit you are rebasing onto with --force-rebase + option. + +* git-send-email learned --confirm option to review the Cc: list before + sending the messages out. + +(developers) + +* Test scripts can be run under valgrind. + + +Fixes since v1.6.2 +------------------ + +All of the fixes in v1.6.2.X maintenance series are included in this +release, unless otherwise noted. + +Here are fixes that this release has, but have not been backported to +v1.6.2.X series. + +* .gitignore learned to handle backslash as a quoting mechanism for + comment introduction character "#" (backport by merging dd482ee if + needed). + +* timestamp output in --date=relative mode used to display timestamps that + are long time ago in the default mode; it now uses "N years M months + ago", and "N years ago" (backport by picking 10edf37 if needed). + +* git-add -i/-p now works with non-ASCII pathnames (backport by picking + 8851f48 if needed). + +* "git hash-object -w" did not read from the configuration file from the + correct .git directory (backport by merging 272459a if needed). + +* git-send-email learned to correctly handle multiple Cc: addresses + (backport by merging afe756c if needed). + +--- +exec >/var/tmp/1 +O=v1.6.2-77-g8cc3fe4 +echo O=$(git describe master) +git shortlog --no-merges $O..master ^maint diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 9b559adefc..8d818a2160 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -491,6 +491,12 @@ message, complete the addressing and subject fields, and press send. Gmail ----- +GMail does not appear to have any way to turn off line wrapping in the web +interface, so this will mangle any emails that you send. You can however +use any IMAP email client to connect to the google imap server, and forward +the emails through that. Just make sure to disable line wrapping in that +email client. Alternatively, use "git send-email" instead. + Submitting properly formatted patches via Gmail is simple now that IMAP support is available. First, edit your ~/.gitconfig to specify your account settings: @@ -503,6 +509,9 @@ account settings: port = 993 sslverify = false +You might need to instead use: folder = "[Google Mail]/Drafts" if you get an error +that the "Folder doesn't exist". + Next, ensure that your Gmail settings are correct. In "Settings" the "Use Unicode (UTF-8) encoding for outgoing messages" should be checked. @@ -513,3 +522,4 @@ command to send the patch emails to your Gmail Drafts folder. Go to your Gmail account, open the Drafts folder, find the patch email, fill in the To: and CC: fields and send away! + diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt index e6717af44e..63fc197fbe 100644 --- a/Documentation/blame-options.txt +++ b/Documentation/blame-options.txt @@ -82,7 +82,7 @@ of lines before or after the line given by <start>. Detect moving lines in the file as well. When a commit moves a block of lines in a file (e.g. the original file has A and then B, and the commit changes it to B and - then A), traditional 'blame' algorithm typically blames + then A), the traditional 'blame' algorithm typically blames the lines that were moved up (i.e. B) to the parent and assigns blame to the lines that were moved down (i.e. A) to the child commit. With this option, both groups of lines @@ -98,8 +98,8 @@ commit. files that were modified in the same commit. This is useful when you reorganize your program and move code around across files. When this option is given twice, - the command looks for copies from all other files in the - parent for the commit that creates the file in addition. + the command additionally looks for copies from all other + files in the parent for the commit that creates the file. + <num> is optional but it is the lower bound on the number of alphanumeric characters that git must detect as moving diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 7c129cb24f..ce71838b9e 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -136,7 +136,7 @@ $ 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 +example; this lets the command include the files from subdirectories of `Documentation/` directory. * Considers adding content from all git-*.sh scripts: @@ -145,7 +145,7 @@ subdirectories of `Documentation/` directory. $ git add git-*.sh ------------ + -Because this example lets shell expand the asterisk (i.e. you are +Because this example lets the shell expand the asterisk (i.e. you are listing the files explicitly), it does not consider `subdir/git-foo.sh`. @@ -198,8 +198,8 @@ one deletion). update:: - This shows the status information and gives prompt - "Update>>". When the prompt ends with double '>>', you can + This shows the status information and issues an "Update>>" + prompt. When the prompt ends with double '>>', you can make more than one selection, concatenated with whitespace or comma. Also you can say ranges. E.g. "2-5 7,9" to choose 2,3,4,5,7,9 from the list. If the second number in a range is @@ -238,8 +238,8 @@ add untracked:: patch:: - This lets you choose one path out of 'status' like selection. - After choosing the path, it presents diff between the index + This lets you choose one path out of a 'status' like selection. + After choosing the path, it presents the diff between the index and the working tree file and asks you if you want to stage the change of each hunk. You can say: @@ -263,13 +263,6 @@ diff:: This lets you review what will be committed (i.e. between HEAD and index). -Bugs ----- -The interactive mode does not work with files whose names contain -characters that need C-quoting. `core.quotepath` configuration can be -used to work this limitation around to some degree, but backslash, -double-quote and control characters will still have problems. - SEE ALSO -------- linkgit:git-status[1] diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt index ff307eb270..1e71dd536b 100644 --- a/Documentation/git-am.txt +++ b/Documentation/git-am.txt @@ -27,8 +27,8 @@ OPTIONS ------- <mbox>|<Maildir>...:: The list of mailbox files to read patches from. If you do not - supply this argument, reads from the standard input. If you supply - directories, they'll be treated as Maildirs. + supply this argument, the command reads from the standard input. + If you supply directories, they will be treated as Maildirs. -s:: --signoff:: @@ -48,7 +48,7 @@ OPTIONS preferred encoding if it is not UTF-8). + This was optional in prior versions of git, but now it is the -default. You could use `--no-utf8` to override this. +default. You can use `--no-utf8` to override this. --no-utf8:: Pass `-n` flag to 'git-mailinfo' (see @@ -57,8 +57,8 @@ default. You could use `--no-utf8` to override this. -3:: --3way:: When the patch does not apply cleanly, fall back on - 3-way merge, if the patch records the identity of blobs - it is supposed to apply to, and we have those blobs + 3-way merge if the patch records the identity of blobs + it is supposed to apply to and we have those blobs available locally. --whitespace=<option>:: @@ -121,18 +121,18 @@ the commit, after stripping common prefix "[PATCH <anything>]". It is supposed to describe what the commit is about concisely as a one line text. -The body of the message (iow, after a blank line that terminates -RFC2822 headers) can begin with "Subject: " and "From: " lines -that are different from those of the mail header, to override -the values of these fields. +The body of the message (the rest of the message after the blank line +that terminates the RFC2822 headers) can begin with "Subject: " and +"From: " lines that are different from those of the mail header, +to override the values of these fields. The commit message is formed by the title taken from the "Subject: ", a blank line and the body of the message up to -where the patch begins. Excess whitespaces at the end of the +where the patch begins. Excess whitespace characters at the end of the lines are automatically stripped. The patch is expected to be inline, directly following the -message. Any line that is of form: +message. Any line that is of the form: * three-dashes and end-of-line, or * a line that begins with "diff -", or @@ -141,18 +141,18 @@ message. Any line that is of form: is taken as the beginning of a patch, and the commit log message is terminated before the first occurrence of such a line. -When initially invoking it, you give it names of the mailboxes -to crunch. Upon seeing the first patch that does not apply, it -aborts in the middle,. You can recover from this in one of two ways: +When initially invoking it, you give it the names of the mailboxes +to process. Upon seeing the first patch that does not apply, it +aborts in the middle. You can recover from this in one of two ways: -. skip the current patch by re-running the command with '--skip' +. skip the current patch by re-running the command with the '--skip' option. . hand resolve the conflict in the working directory, and update - the index file to bring it in a state that the patch should - have produced. Then run the command with '--resolved' option. + the index file to bring it into a state that the patch should + have produced. Then run the command with the '--resolved' option. -The command refuses to process new mailboxes while `.git/rebase-apply` +The command refuses to process new mailboxes while the `.git/rebase-apply` directory exists, so if you decide to start over from scratch, run `rm -f -r .git/rebase-apply` before running the command with mailbox names. diff --git a/Documentation/git-annotate.txt b/Documentation/git-annotate.txt index 0aba022ba6..0590eec056 100644 --- a/Documentation/git-annotate.txt +++ b/Documentation/git-annotate.txt @@ -3,7 +3,7 @@ git-annotate(1) NAME ---- -git-annotate - Annotate file lines with commit info +git-annotate - Annotate file lines with commit information SYNOPSIS -------- @@ -12,11 +12,11 @@ SYNOPSIS DESCRIPTION ----------- Annotates each line in the given file with information from the commit -which introduced the line. Optionally annotate from a given revision. +which introduced the line. Optionally annotates from a given revision. The only difference between this command and linkgit:git-blame[1] is that they use slightly different output formats, and this command exists only -for backward compatibility to support existing scripts, and provide more +for backward compatibility to support existing scripts, and provide a more familiar command name for people coming from other SCM systems. OPTIONS diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index 9400f6a5d0..9e5baa2777 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -25,7 +25,7 @@ and a work tree. OPTIONS ------- <patch>...:: - The files to read patch from. '-' can be used to read + The files to read the patch from. '-' can be used to read from the standard input. --stat:: @@ -33,8 +33,8 @@ OPTIONS input. Turns off "apply". --numstat:: - Similar to \--stat, but shows number of added and - deleted lines in decimal notation and pathname without + Similar to \--stat, but shows the number of added and + deleted lines in decimal notation and the pathname without abbreviation, to make it more machine friendly. For binary files, outputs two `-` instead of saying `0 0`. Turns off "apply". @@ -60,15 +60,15 @@ OPTIONS causes the index file to be updated. --cached:: - Apply a patch without touching the working tree. Instead, take the - cached data, apply the patch, and store the result in the index, + Apply a patch without touching the working tree. Instead take the + cached data, apply the patch, and store the result in the index without using the working tree. This implies '--index'. --build-fake-ancestor=<file>:: Newer 'git-diff' output has embedded 'index information' for each blob to help identify the original version that the patch applies to. When this flag is given, and if - the original versions of the blobs is available locally, + the original versions of the blobs are available locally, builds a temporary index containing those blobs. + When a pure mode change is encountered (which has no index information), @@ -109,13 +109,13 @@ the information is read from the current index instead. applying a diff generated with --unified=0. To bypass these checks use '--unidiff-zero'. + -Note, for the reasons stated above usage of context-free patches are +Note, for the reasons stated above usage of context-free patches is discouraged. --apply:: If you use any of the options marked "Turns off 'apply'" above, 'git-apply' reads and outputs the - information you asked without actually applying the + requested information without actually applying the patch. Give this flag after those flags to also apply the patch. @@ -124,7 +124,7 @@ discouraged. patch. This can be used to extract the common part between two files by first running 'diff' on them and applying the result with this option, which would apply the - deletion part but not addition part. + deletion part but not the addition part. --allow-binary-replacement:: --binary:: @@ -159,10 +159,10 @@ on the command line, and ignored if there is any include pattern. considered whitespace errors. + By default, the command outputs warning messages but applies the patch. -When `git-apply is used for statistics and not applying a +When `git-apply` is used for statistics and not applying a patch, it defaults to `nowarn`. + -You can use different `<action>` to control this +You can use different `<action>` values to control this behavior: + * `nowarn` turns off the trailing whitespace warning. @@ -170,7 +170,7 @@ behavior: patch as-is (default). * `fix` outputs warnings for a few such errors, and applies the patch after fixing them (`strip` is a synonym --- the tool - used to consider only trailing whitespaces as errors, and the + used to consider only trailing whitespace characters as errors, and the fix involved 'stripping' them, but modern gits do more). * `error` outputs warnings for a few such errors, and refuses to apply the patch. @@ -195,7 +195,7 @@ behavior: adjusting the hunk headers appropriately). --directory=<root>:: - Prepend <root> to all filenames. If a "-p" argument was passed, too, + Prepend <root> to all filenames. If a "-p" argument was also passed, it is applied before prepending the new root. + For example, a patch that talks about updating `a/git-gui.sh` to `b/git-gui.sh` @@ -221,7 +221,7 @@ ignored, i.e., they are not required to be up-to-date or clean and they are not updated. If --index is not specified, then the submodule commits in the patch -are ignored and only the absence of presence of the corresponding +are ignored and only the absence or presence of the corresponding subdirectory is checked and (if possible) updated. Author diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt index 41cbf9c081..0eeefe0060 100644 --- a/Documentation/git-archive.txt +++ b/Documentation/git-archive.txt @@ -10,6 +10,7 @@ SYNOPSIS -------- [verse] 'git archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>] + [--output=<file>] [--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish> [path...] @@ -47,6 +48,9 @@ OPTIONS --prefix=<prefix>/:: Prepend <prefix>/ to each filename in the archive. +--output=<file>:: + Write the archive to <file> instead of stdout. + <extra>:: This can be any options that the archiver backend understand. See next section. @@ -88,6 +92,18 @@ tar.umask:: archiving user's umask will be used instead. See umask(2) for details. +ATTRIBUTES +---------- + +export-ignore:: + Files and directories with the attribute export-ignore won't be + added to archive files. See linkgit:gitattributes[5] for details. + +export-subst:: + If the attribute export-subst is set for a file then git will + expand several placeholders when adding this file to an archive. + See linkgit:gitattributes[5] for details. + EXAMPLES -------- git archive --format=tar --prefix=junk/ HEAD | (cd /var/tmp/ && tar xf -):: @@ -110,6 +126,11 @@ git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs Put everything in the current head's Documentation/ directory into 'git-1.4.0-docs.zip', with the prefix 'git-docs/'. + +SEE ALSO +-------- +linkgit:gitattributes[5] + Author ------ Written by Franck Bui-Huu and Rene Scharfe. diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 147ea38197..e65c1cae8b 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -212,7 +212,7 @@ If you have a script that can tell if the current source code is good or bad, you can automatically bisect using: ------------ -$ git bisect run my_script +$ git bisect run my_script arguments ------------ Note that the "run" script (`my_script` in the above example) should @@ -252,6 +252,13 @@ $ git bisect start HEAD v1.2 -- # HEAD is bad, v1.2 is good $ git bisect run make # "make" builds the app ------------ +* Automatically bisect a test failure between origin and HEAD: ++ +------------ +$ git bisect start HEAD origin -- # HEAD is bad, origin is good +$ git bisect run make test # "make test" builds and tests +------------ + * Automatically bisect a broken test suite: + ------------ @@ -291,6 +298,15 @@ 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. +* Automatically bisect a broken test suite: ++ +------------ +$ git bisect start HEAD HEAD~10 -- # culprit is among the last 10 +$ git bisect run sh -c "make || exit 125; ~/check_test_case.sh" +------------ ++ +Does the same as the previous example, but on a single line. + Author ------ Written by Linus Torvalds <torvalds@osdl.org> diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 3bccffae62..125d8f3c32 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -8,7 +8,7 @@ git-checkout - Checkout a branch or paths to the working tree SYNOPSIS -------- [verse] -'git checkout' [-q] [-f] [--track | --no-track] [-b <new_branch> [-l]] [-m] [<branch>] +'git checkout' [-q] [-f] [-t | --track | --no-track] [-b <new_branch> [-l]] [-m] [<branch>] 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>... DESCRIPTION @@ -21,15 +21,15 @@ specified, <new_branch>. Using -b will cause <new_branch> to be created; in this case you can use the --track or --no-track options, which will be passed to `git branch`. -As a convenience, --track will default to create a branch whose +As a convenience, --track will default to creating a branch whose name is constructed from the specified branch name by stripping the first namespace level. When <paths> are given, this command does *not* switch branches. It updates the named paths in the working tree from the index file, or from a named <tree-ish> (most often a commit). In -this case, the `-b` options is meaningless and giving -either of them results in an error. <tree-ish> argument can be +this case, the `-b` and `--track` options are meaningless and giving +either of them results in an error. The <tree-ish> argument can be used to specify a specific tree-ish (i.e. commit, tag or tree) to update the index for the given paths before updating the working tree. @@ -75,14 +75,13 @@ entries; instead, unmerged entries are ignored. <repository> <refspec>" explicitly. This behavior is the default when the start point is a remote branch. Set the branch.autosetupmerge configuration variable to `false` if you want - 'git-checkout' and 'git-branch' to always behave as if '--no-track' were + 'git checkout' and 'git branch' to always behave as if '--no-track' were given. Set it to `always` if you want this behavior when the - start-point is either a local or remote branch. + start point is either a local or remote branch. + -If no '-b' option was given, the name of the new branch will be -derived from the remote branch, by attempting to guess the name -of the branch on remote system. If "remotes/" or "refs/remotes/" -are prefixed, it is stripped away, and then the part up to the +If no '-b' option is given, the name of the new branch will be +derived from the remote branch. If "remotes/" or "refs/remotes/" +is prefixed it is stripped away, and then the part up to the next slash (which would be the nickname of the remote) is removed. This would tell us to use "hack" as the local branch when branching off of "origin/hack" (or "remotes/origin/hack", or even @@ -152,12 +151,12 @@ $ git checkout v2.6.18 ------------ Earlier versions of git did not allow this and asked you to -create a temporary branch using `-b` option, but starting from +create a temporary branch using the `-b` option, but starting from version 1.5.0, the above command 'detaches' your HEAD from the -current branch and directly point at the commit named by the tag -(`v2.6.18` in the above example). +current branch and directly points at the commit named by the tag +(`v2.6.18` in the example above). -You can use usual git commands while in this state. You can use +You can use all git commands while in this state. You can use `git reset --hard $othercommit` to further move around, for example. You can make changes and create a new commit on top of a detached HEAD. You can even create a merge by using `git @@ -191,7 +190,7 @@ $ git checkout hello.c <3> ------------ + <1> switch branch -<2> take out a file out of other commit +<2> take a file out of another commit <3> restore hello.c from HEAD of current branch + If you have an unfortunate branch that is named `hello.c`, this @@ -202,7 +201,7 @@ You should instead write: $ git checkout -- hello.c ------------ -. After working in a wrong branch, switching to the correct +. After working in the wrong branch, switching to the correct branch would be done using: + ------------ @@ -210,7 +209,7 @@ $ git checkout mytopic ------------ + However, your "wrong" branch and correct "mytopic" branch may -differ in files that you have locally modified, in which case, +differ in files that you have modified locally, in which case the above checkout would fail like this: + ------------ diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 19a8917b83..82ce89eae8 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -22,6 +22,7 @@ SYNOPSIS 'git config' [<file-option>] [-z|--null] -l | --list 'git config' [<file-option>] --get-color name [default] 'git config' [<file-option>] --get-colorbool name [stdout-is-tty] +'git config' [<file-option>] -e | --edit DESCRIPTION ----------- @@ -130,6 +131,10 @@ See also <<FILES>>. in the config file will cause the value to be multiplied by 1024, 1048576, or 1073741824 prior to output. +--bool-or-int:: + 'git-config' will ensure that the output matches the format of + either --bool or --int, as described above. + -z:: --null:: For all options that output values and/or keys, always @@ -157,6 +162,11 @@ See also <<FILES>>. output. The optional `default` parameter is used instead, if there is no color configured for `name`. +-e:: +--edit:: + Opens an editor to modify the specified config file; either + '--system', '--global', or repository (default). + [[FILES]] FILES ----- diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index 11a7d77261..e7ae8cf109 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -10,7 +10,8 @@ SYNOPSIS -------- [verse] 'git format-patch' [-k] [-o <dir> | --stdout] [--thread] - [--attach[=<boundary>] | --inline[=<boundary>]] + [--attach[=<boundary>] | --inline[=<boundary>] | + [--no-attach]] [-s | --signoff] [<common diff options>] [-n | --numbered | -N | --no-numbered] [--start-number <n>] [--numbered-files] @@ -117,6 +118,10 @@ include::diff-options.txt[] which is the commit message and the patch itself in the second part, with "Content-Disposition: attachment". +--no-attach:: + Disable the creation of an attachment, overriding the + configuration setting. + --inline[=<boundary>]:: Create multipart/mixed attachment, the first part of which is the commit message and the patch itself in the @@ -174,7 +179,8 @@ CONFIGURATION ------------- You can specify extra mail header lines to be added to each message in the repository configuration, new defaults for the subject prefix -and file suffix, and number patches when outputting more than one. +and file suffix, control attachements, and number patches when outputting +more than one. ------------ [format] @@ -183,6 +189,7 @@ and file suffix, and number patches when outputting more than one. suffix = .txt numbered = auto cc = <email> + attach [ = mime-boundary-string ] ------------ diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt index 1685f04efe..024084b8b7 100644 --- a/Documentation/git-imap-send.txt +++ b/Documentation/git-imap-send.txt @@ -64,6 +64,13 @@ imap.sslverify:: used by the SSL/TLS connection. Default is `true`. Ignored when imap.tunnel is set. +imap.preformattedHTML:: + A boolean to enable/disable the use of html encoding when sending + a patch. An html encoded patch will be bracketed with <pre> + and have a content type of text/html. Ironically, enabling this + option causes Thunderbird to send the patch as a plain/text, + format=fixed email. Default is `false`. + Examples ~~~~~~~~ diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index ac6421178c..4e7e5a719a 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -48,17 +48,19 @@ push. Arbitrary expressions cannot be used here, an actual ref must be named. If `:`<dst> is omitted, the same ref as <src> will be updated. + -The object referenced by <src> is used to fast forward the ref <dst> -on the remote side. If the optional leading plus `{plus}` is used, the -remote ref is updated even if it does not result in a fast forward -update. +The object referenced by <src> is used to update the <dst> reference +on the remote side, but by default this is only allowed if the +update can fast forward <dst>. By having the optional leading `{plus}`, +you can tell git to update the <dst> ref even when the update is not a +fast forward. This does *not* attempt to merge <src> into <dst>. See +EXAMPLES below for details. + `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`. + Pushing an empty <src> allows you to delete the <dst> ref from the remote repository. + -The special refspec `:` (or `+:` to allow non-fast forward updates) +The special refspec `:` (or `{plus}:` to allow non-fast forward updates) directs git to push "matching" branches: for every branch that exists on the local side, the remote side is updated if a branch of the same name already exists on the remote side. This is the default operation mode @@ -218,6 +220,30 @@ git push origin :experimental:: Find a ref that matches `experimental` in the `origin` repository (e.g. `refs/heads/experimental`), and delete it. +git push origin {plus}dev:master:: + Update the origin repository's master branch with the dev branch, + allowing non-fast forward updates. *This can leave unreferenced + commits dangling in the origin repository.* Consider the + following situation, where a fast forward is not possible: ++ +---- + o---o---o---A---B origin/master + \ + X---Y---Z dev +---- ++ +The above command would change the origin repository to ++ +---- + A---B (unnamed branch) + / + o---o---o---X---Y---Z master +---- ++ +Commits A and B would no longer belong to a branch with a symbolic name, +and so would be unreachable. As such, these commits would be removed by +a `git gc` command on the origin repository. + Author ------ diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 30487de48f..da3c38cd60 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -243,7 +243,7 @@ OPTIONS context exist they all must match. By default no context is ever ignored. ---whitespace=<nowarn|warn|error|error-all|strip>:: +--whitespace=<option>:: This flag is passed to the 'git-apply' program (see linkgit:git-apply[1]) that applies the patch. Incompatible with the --interactive option. diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 66bf3b2fcd..14dfb501eb 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -177,14 +177,25 @@ Automating --suppress-cc:: Specify an additional category of recipients to suppress the - auto-cc of. 'self' will avoid including the sender, 'author' will - avoid including the patch author, 'cc' will avoid including anyone - mentioned in Cc lines in the patch, 'sob' will avoid including - anyone mentioned in Signed-off-by lines, and 'cccmd' will avoid - running the --cc-cmd. 'all' will suppress all auto cc values. - Default is the value of 'sendemail.suppresscc' configuration value; - if that is unspecified, default to 'self' if --suppress-from is - specified, as well as 'sob' if --no-signed-off-cc is specified. + auto-cc of: ++ +-- +- 'author' will avoid including the patch author +- 'self' will avoid including the sender +- 'cc' will avoid including anyone mentioned in Cc lines in the patch header + except for self (use 'self' for that). +- 'ccbody' will avoid including anyone mentioned in Cc lines in the + patch body (commit message) except for self (use 'self' for that). +- 'sob' will avoid including anyone mentioned in Signed-off-by lines except + for self (use 'self' for that). +- 'cccmd' will avoid running the --cc-cmd. +- 'body' is equivalent to 'sob' + 'ccbody' +- 'all' will suppress all auto cc values. +-- ++ +Default is the value of 'sendemail.suppresscc' configuration value; if +that is unspecified, default to 'self' if --suppress-from is +specified, as well as 'body' if --no-signed-off-cc is specified. --[no-]suppress-from:: If this is set, do not add the From: address to the cc: list. @@ -201,6 +212,22 @@ Automating Administering ~~~~~~~~~~~~~ +--confirm:: + Confirm just before sending: ++ +-- +- 'always' will always confirm before sending +- 'never' will never confirm before sending +- 'cc' will confirm before sending when send-email has automatically + added addresses from the patch to the Cc list +- 'compose' will confirm before sending the first message when using --compose. +- 'auto' is equivalent to 'cc' + 'compose' +-- ++ +Default is the value of 'sendemail.confirm' configuration value; if that +is unspecified, default to 'auto' unless any of the suppress options +have been specified, in which case default to 'compose'. + --dry-run:: Do everything except actually send the emails. @@ -244,6 +271,11 @@ sendemail.multiedit:: summary when '--compose' is used). If false, files will be edited one after the other, spawning a new editor each time. +sendemail.confirm:: + Sets the default for whether to confirm before sending. Must be + one of 'always', 'never', 'cc', 'compose', or 'auto'. See '--confirm' + in the previous section for the meaning of these values. + Author ------ diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 3d456545d7..cda3389331 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -169,6 +169,10 @@ and have no uncommitted changes. reused if a user is later given access to an alternate transport method (e.g. `svn+ssh://` or `https://`) for commit. +config key: svn-remote.<name>.commiturl + +config key: svn.commiturl (overwrites all svn-remote.<name>.commiturl options) + Using this option for any other purpose (don't ask) is very strongly discouraged. -- diff --git a/Documentation/git.txt b/Documentation/git.txt index 0c7bba3fa9..9a26bde73e 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,6 +43,11 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: +* link:v1.6.2/git.html[documentation for release 1.6.2] + +* release notes for + link:RelNotes-1.6.2.txt[1.6.2]. + * link:v1.6.1.3/git.html[documentation for release 1.6.1.3] * release notes for diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 227934f59a..55668e345f 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -18,10 +18,10 @@ A `gitattributes` file is a simple text file that gives Each line in `gitattributes` file is of form: - glob attr1 attr2 ... + pattern attr1 attr2 ... -That is, a glob pattern followed by an attributes list, -separated by whitespaces. When the glob pattern matches the +That is, a pattern followed by an attributes list, +separated by whitespaces. When the pattern matches the path in question, the attributes listed on the line are given to the path. @@ -48,13 +48,14 @@ Set to a value:: Unspecified:: - No glob pattern matches the path, and nothing says if + No pattern matches the path, and nothing says if the path has or does not have the attribute, the attribute for the path is said to be Unspecified. -When more than one glob pattern matches the path, a later line +When more than one pattern matches the path, a later line overrides an earlier line. This overriding is done per -attribute. +attribute. The rules how the pattern matches paths are the +same as in `.gitignore` files; see linkgit:gitignore[5]. When deciding what attributes are assigned to a path, git consults `$GIT_DIR/info/attributes` file (which has the highest diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt index bd005bc5c8..cf465cb47e 100644 --- a/Documentation/gitk.txt +++ b/Documentation/gitk.txt @@ -74,7 +74,7 @@ frequently used options. <path>...:: Limit commits to the ones touching files in the given paths. Note, to - avoid ambiguity wrt. revision names use "--" to separate the paths + avoid ambiguity with respect to revision names use "--" to separate the paths from any preceding options. Examples diff --git a/Documentation/howto/revert-a-faulty-merge.txt b/Documentation/howto/revert-a-faulty-merge.txt index 39b1da440a..3b4a390005 100644 --- a/Documentation/howto/revert-a-faulty-merge.txt +++ b/Documentation/howto/revert-a-faulty-merge.txt @@ -39,7 +39,7 @@ Such a "revert" of a merge can be made with: $ git revert -m 1 M -After the develpers of the side branch fixes their mistakes, the history +After the developers of the side branch fix their mistakes, the history may look like this: ---o---o---o---M---x---x---W---x @@ -116,7 +116,7 @@ If you reverted the revert in such a case as in the previous example: / \ / ---A---B A'--B'--C' -where Y is the revert of W, A' and B'are rerolled A and B, and there may +where Y is the revert of W, A' and B' are rerolled A and B, and there may also be a further fix-up C' on the side branch. "diff Y^..Y" is similar to "diff -R W^..W" (which in turn means it is similar to "diff M^..M"), and "diff A'^..C'" by definition would be similar but different from that, diff --git a/Documentation/howto/setup-git-server-over-http.txt b/Documentation/howto/setup-git-server-over-http.txt index 4032748608..622ee5c8dd 100644 --- a/Documentation/howto/setup-git-server-over-http.txt +++ b/Documentation/howto/setup-git-server-over-http.txt @@ -143,7 +143,7 @@ Then, add something like this to your httpd.conf Require valid-user </Location> - Debian automatically reads all files under /etc/apach2/conf.d. + Debian automatically reads all files under /etc/apache2/conf.d. The password file can be somewhere else, but it has to be readable by Apache and preferably not readable by the world. diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 159390c35a..5c6e678aa3 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -152,3 +152,12 @@ $ git log -2 --pretty=tformat:%h 4da45bef \ 4da45be 7134973 --------------------- ++ +In addition, any unrecognized string that has a `%` in it is interpreted +as if it has `tformat:` in front of it. For example, these two are +equivalent: ++ +--------------------- +$ git log -2 --pretty=tformat:%h 4da45bef +$ git log -2 --pretty=%h 4da45bef +--------------------- diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt index 5f21efe407..bff94991b6 100644 --- a/Documentation/pretty-options.txt +++ b/Documentation/pretty-options.txt @@ -1,4 +1,5 @@ --pretty[='<format>']:: +--format[='<format>']:: Pretty-print the contents of the commit logs in a given format, where '<format>' can be one of 'oneline', 'short', 'medium', @@ -17,6 +18,10 @@ configuration (see linkgit:git-config[1]). This should make "--pretty=oneline" a whole lot more readable for people using 80-column terminals. +--oneline:: + This is a shorthand for "--pretty=oneline --abbrev-commit" + used together. + --encoding[=<encoding>]:: The commit objects record the encoding used for the log message in their encoding header; this option can be used to tell the diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 5076322877..7dd237c2f6 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -568,11 +568,11 @@ This outputs all the commit objects between the included and excluded commits, ordered by their distance to the included and excluded commits. The farthest from them is displayed first. (This is the only one displayed by `--bisect`.) - ++ This is useful because it makes it easy to choose a good commit to test when you want to avoid to test some of them for some reason (they may not compile for example). - ++ This option can be used along with `--bisect-vars`, in this case, after all the sorted commit objects, there will be the same text as if `--bisect-vars` had been used alone. diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt index 539863b1f9..e66ca9f70c 100644 --- a/Documentation/technical/api-parse-options.txt +++ b/Documentation/technical/api-parse-options.txt @@ -66,6 +66,12 @@ Steps to parse options non-option arguments in `argv[]`. `argc` is updated appropriately because of the assignment. + +You can also pass NULL instead of a usage array as fourth parameter of +parse_options(), to avoid displaying a help screen with usage info and +option list. This should only be done if necessary, e.g. to implement +a limited parser for only a subset of the options that needs to be run +before the full parser, which in turn shows the full help message. ++ Flags are the bitwise-or of: `PARSE_OPT_KEEP_DASHDASH`:: @@ -77,6 +83,28 @@ Flags are the bitwise-or of: Using this flag, processing is stopped at the first non-option argument. +`PARSE_OPT_KEEP_ARGV0`:: + Keep the first argument, which contains the program name. It's + removed from argv[] by default. + +`PARSE_OPT_KEEP_UNKNOWN`:: + Keep unknown arguments instead of erroring out. This doesn't + work for all combinations of arguments as users might expect + it to do. E.g. if the first argument in `--unknown --known` + takes a value (which we can't know), the second one is + mistakenly interpreted as a known option. Similarly, if + `PARSE_OPT_STOP_AT_NON_OPTION` is set, the second argument in + `--unknown value` will be mistakenly interpreted as a + non-option, not as a value belonging to the unknown option, + the parser early. That's why parse_options() errors out if + both options are set. + +`PARSE_OPT_NO_INTERNAL_HELP`:: + By default, parse_options() handles `-h`, `--help` and + `--help-all` internally, by showing a help screen. This option + turns it off and allows one to add custom handlers for these + options, or to just leave them unknown. + Data Structure -------------- diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt index ac56d1c477..7438149249 100644 --- a/Documentation/technical/api-strbuf.txt +++ b/Documentation/technical/api-strbuf.txt @@ -222,7 +222,7 @@ which can be used by the programmer of the callback as she sees fit. Read a given size of data from a FILE* pointer to the buffer. + -NOTE: The buffer is rewinded if the read fails. If -1 is returned, +NOTE: The buffer is rewound if the read fails. If -1 is returned, `errno` must be consulted, like you would do for `read(3)`. `strbuf_read()`, `strbuf_read_file()` and `strbuf_getline()` has the same behaviour as well. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 9c9fe640fb..97fc1e0519 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.6.1.GIT +DEF_VER=v1.6.2.GIT LF=' ' @@ -1468,8 +1468,8 @@ endif bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \ execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \ { $(RM) "$$execdir/git-add$X" && \ - ln git-add$X "$$execdir/git-add$X" 2>/dev/null || \ - cp git-add$X "$$execdir/git-add$X"; } && \ + ln "$$bindir/git$X" "$$execdir/git-add$X" 2>/dev/null || \ + cp "$$bindir/git$X" "$$execdir/git-add$X"; } && \ { for p in $(filter-out git-add$X,$(BUILT_INS)); do \ $(RM) "$$execdir/$$p" && \ ln "$$execdir/git-add$X" "$$execdir/$$p" 2>/dev/null || \ @@ -1640,3 +1640,27 @@ check-docs:: check-builtins:: ./check-builtins.sh +### Test suite coverage testing +# +.PHONY: coverage coverage-clean coverage-build coverage-report + +coverage: + $(MAKE) coverage-build + $(MAKE) coverage-report + +coverage-clean: + rm -f *.gcda *.gcno + +COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs +COVERAGE_LDFLAGS = $(CFLAGS) -O0 -lgcov + +coverage-build: coverage-clean + $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" all + $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" \ + -j1 test + +coverage-report: + gcov -b *.c + grep '^function.*called 0 ' *.c.gcov \ + | sed -e 's/\([^:]*\)\.gcov: *function \([^ ]*\) called.*/\1: \2/' \ + | tee coverage-untested-functions @@ -24,10 +24,18 @@ It was originally written by Linus Torvalds with help of a group of hackers around the net. It is currently maintained by Junio C Hamano. Please read the file INSTALL for installation instructions. + See Documentation/gittutorial.txt to get started, then see -Documentation/everyday.txt for a useful minimum set of commands, -and "man git-commandname" for documentation of each command. -CVS users may also want to read Documentation/cvs-migration.txt. +Documentation/everyday.txt for a useful minimum set of commands, and +Documentation/git-commandname.txt for documentation of each command. +If git has been correctly installed, then the tutorial can also be +read with "man gittutorial" or "git help tutorial", and the +documentation of each command with "man git-commandname" or "git help +commandname". + +CVS users may also want to read Documentation/gitcvs-migration.txt +("man gitcvs-migration" or "git help cvs-migration" if git is +installed). Many Git online resources are accessible from http://git.or.cz/ including full documentation and Git related tools. @@ -1 +1 @@ -Documentation/RelNotes-1.6.2.txt
\ No newline at end of file +Documentation/RelNotes-1.6.3.txt
\ No newline at end of file @@ -253,6 +253,7 @@ static int parse_archive_args(int argc, const char **argv, const char *base = NULL; const char *remote = NULL; const char *exec = NULL; + const char *output = NULL; int compression_level = -1; int verbose = 0; int i; @@ -262,6 +263,8 @@ static int parse_archive_args(int argc, const char **argv, OPT_STRING(0, "format", &format, "fmt", "archive format"), OPT_STRING(0, "prefix", &base, "prefix", "prepend prefix to each pathname in the archive"), + OPT_STRING(0, "output", &output, "file", + "write the archive to this file"), OPT__VERBOSE(&verbose), OPT__COMPR('0', &compression_level, "store only", 0), OPT__COMPR('1', &compression_level, "compress faster", 1), @@ -290,6 +293,8 @@ static int parse_archive_args(int argc, const char **argv, die("Unexpected option --remote"); if (exec) die("Option --exec can only be used together with --remote"); + if (output) + die("Unexpected option --output"); if (!base) base = ""; diff --git a/builtin-add.c b/builtin-add.c index ac98c8354d..08443f2f1e 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -15,7 +15,7 @@ static const char * const builtin_add_usage[] = { "git add [options] [--] <filepattern>...", NULL }; -static int patch_interactive = 0, add_interactive = 0; +static int patch_interactive, add_interactive; static int take_worktree_changes; static void fill_pathspec_matches(const char **pathspec, char *seen, int specs) diff --git a/builtin-archive.c b/builtin-archive.c index 5ceec433fd..60adef9363 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -5,44 +5,35 @@ #include "cache.h" #include "builtin.h" #include "archive.h" +#include "parse-options.h" #include "pkt-line.h" #include "sideband.h" -static int run_remote_archiver(const char *remote, int argc, - const char **argv) +static void create_output_file(const char *output_file) +{ + int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if (output_fd < 0) + die("could not create archive file: %s ", output_file); + if (output_fd != 1) { + if (dup2(output_fd, 1) < 0) + die("could not redirect output"); + else + close(output_fd); + } +} + +static int run_remote_archiver(int argc, const char **argv, + const char *remote, const char *exec) { char *url, buf[LARGE_PACKET_MAX]; int fd[2], i, len, rv; struct child_process *conn; - const char *exec = "git-upload-archive"; - int exec_at = 0, exec_value_at = 0; - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!prefixcmp(arg, "--exec=")) { - if (exec_at) - die("multiple --exec specified"); - exec = arg + 7; - exec_at = i; - } else if (!strcmp(arg, "--exec")) { - if (exec_at) - die("multiple --exec specified"); - if (i + 1 >= argc) - die("option --exec requires a value"); - exec = argv[i + 1]; - exec_at = i; - exec_value_at = ++i; - } - } url = xstrdup(remote); conn = git_connect(fd, url, exec, 0); - for (i = 1; i < argc; i++) { - if (i == exec_at || i == exec_value_at) - continue; + for (i = 1; i < argc; i++) packet_write(fd[1], "argument %s\n", argv[i]); - } packet_flush(fd[1]); len = packet_read_line(fd[0], buf, sizeof(buf)); @@ -69,51 +60,33 @@ static int run_remote_archiver(const char *remote, int argc, return !!rv; } -static const char *extract_remote_arg(int *ac, const char **av) -{ - int ix, iy, cnt = *ac; - int no_more_options = 0; - const char *remote = NULL; - - for (ix = iy = 1; ix < cnt; ix++) { - const char *arg = av[ix]; - if (!strcmp(arg, "--")) - no_more_options = 1; - if (!no_more_options) { - if (!prefixcmp(arg, "--remote=")) { - if (remote) - die("Multiple --remote specified"); - remote = arg + 9; - continue; - } else if (!strcmp(arg, "--remote")) { - if (remote) - die("Multiple --remote specified"); - if (++ix >= cnt) - die("option --remote requires a value"); - remote = av[ix]; - continue; - } - if (arg[0] != '-') - no_more_options = 1; - } - if (ix != iy) - av[iy] = arg; - iy++; - } - if (remote) { - av[--cnt] = NULL; - *ac = cnt; - } - return remote; -} +#define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | \ + PARSE_OPT_KEEP_ARGV0 | \ + PARSE_OPT_KEEP_UNKNOWN | \ + PARSE_OPT_NO_INTERNAL_HELP ) int cmd_archive(int argc, const char **argv, const char *prefix) { + const char *exec = "git-upload-archive"; + const char *output = NULL; const char *remote = NULL; + struct option local_opts[] = { + OPT_STRING(0, "output", &output, "file", + "write the archive to this file"), + OPT_STRING(0, "remote", &remote, "repo", + "retrieve the archive from remote repository <repo>"), + OPT_STRING(0, "exec", &exec, "cmd", + "path to the remote git-upload-archive command"), + OPT_END() + }; + + argc = parse_options(argc, argv, local_opts, NULL, PARSE_OPT_KEEP_ALL); + + if (output) + create_output_file(output); - remote = extract_remote_arg(&argc, argv); if (remote) - return run_remote_archiver(remote, argc, argv); + return run_remote_archiver(argc, argv, remote, exec); setvbuf(stderr, NULL, _IOLBF, BUFSIZ); diff --git a/builtin-blame.c b/builtin-blame.c index 0c6ad98d19..2aedd17c39 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -78,6 +78,7 @@ static unsigned blame_copy_score; */ struct origin { int refcnt; + struct origin *previous; struct commit *commit; mmfile_t file; unsigned char blob_sha1[20]; @@ -119,6 +120,8 @@ static inline struct origin *origin_incref(struct origin *o) static void origin_decref(struct origin *o) { if (o && --o->refcnt <= 0) { + if (o->previous) + origin_decref(o->previous); free(o->file.ptr); free(o); } @@ -1202,6 +1205,10 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) struct origin *porigin = sg_origin[i]; if (!porigin) continue; + if (!origin->previous) { + origin_incref(porigin); + origin->previous = porigin; + } if (pass_blame_to_parent(sb, origin, porigin)) goto finish; } @@ -1419,6 +1426,39 @@ static void write_filename_info(const char *path) } /* + * Porcelain/Incremental format wants to show a lot of details per + * commit. Instead of repeating this every line, emit it only once, + * the first time each commit appears in the output. + */ +static int emit_one_suspect_detail(struct origin *suspect) +{ + struct commit_info ci; + + if (suspect->commit->object.flags & METAINFO_SHOWN) + return 0; + + suspect->commit->object.flags |= METAINFO_SHOWN; + get_commit_info(suspect->commit, &ci, 1); + printf("author %s\n", ci.author); + printf("author-mail %s\n", ci.author_mail); + printf("author-time %lu\n", ci.author_time); + printf("author-tz %s\n", ci.author_tz); + printf("committer %s\n", ci.committer); + printf("committer-mail %s\n", ci.committer_mail); + printf("committer-time %lu\n", ci.committer_time); + printf("committer-tz %s\n", ci.committer_tz); + printf("summary %s\n", ci.summary); + if (suspect->commit->object.flags & UNINTERESTING) + printf("boundary\n"); + if (suspect->previous) { + struct origin *prev = suspect->previous; + printf("previous %s ", sha1_to_hex(prev->commit->object.sha1)); + write_name_quoted(prev->path, stdout, '\n'); + } + return 1; +} + +/* * The blame_entry is found to be guilty for the range. Mark it * as such, and show it in incremental output. */ @@ -1433,22 +1473,7 @@ static void found_guilty_entry(struct blame_entry *ent) printf("%s %d %d %d\n", sha1_to_hex(suspect->commit->object.sha1), ent->s_lno + 1, ent->lno + 1, ent->num_lines); - if (!(suspect->commit->object.flags & METAINFO_SHOWN)) { - struct commit_info ci; - suspect->commit->object.flags |= METAINFO_SHOWN; - get_commit_info(suspect->commit, &ci, 1); - printf("author %s\n", ci.author); - printf("author-mail %s\n", ci.author_mail); - printf("author-time %lu\n", ci.author_time); - printf("author-tz %s\n", ci.author_tz); - printf("committer %s\n", ci.committer); - printf("committer-mail %s\n", ci.committer_mail); - printf("committer-time %lu\n", ci.committer_time); - printf("committer-tz %s\n", ci.committer_tz); - printf("summary %s\n", ci.summary); - if (suspect->commit->object.flags & UNINTERESTING) - printf("boundary\n"); - } + emit_one_suspect_detail(suspect); write_filename_info(suspect->path); maybe_flush_or_die(stdout, "stdout"); } @@ -1551,24 +1576,8 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent) ent->s_lno + 1, ent->lno + 1, ent->num_lines); - if (!(suspect->commit->object.flags & METAINFO_SHOWN)) { - struct commit_info ci; - suspect->commit->object.flags |= METAINFO_SHOWN; - get_commit_info(suspect->commit, &ci, 1); - printf("author %s\n", ci.author); - printf("author-mail %s\n", ci.author_mail); - printf("author-time %lu\n", ci.author_time); - printf("author-tz %s\n", ci.author_tz); - printf("committer %s\n", ci.committer); - printf("committer-mail %s\n", ci.committer_mail); - printf("committer-time %lu\n", ci.committer_time); - printf("committer-tz %s\n", ci.committer_tz); - write_filename_info(suspect->path); - printf("summary %s\n", ci.summary); - if (suspect->commit->object.flags & UNINTERESTING) - printf("boundary\n"); - } - else if (suspect->commit->object.flags & MORE_THAN_ONE_PATH) + if (emit_one_suspect_detail(suspect) || + (suspect->commit->object.flags & MORE_THAN_ONE_PATH)) write_filename_info(suspect->path); cp = nth_line(sb, ent->lno); @@ -1806,36 +1815,6 @@ static void sanity_check_refcnt(struct scoreboard *sb) baa = 1; } } - for (ent = sb->ent; ent; ent = ent->next) { - /* Mark the ones that haven't been checked */ - if (0 < ent->suspect->refcnt) - ent->suspect->refcnt = -ent->suspect->refcnt; - } - for (ent = sb->ent; ent; ent = ent->next) { - /* - * ... then pick each and see if they have the the - * correct refcnt. - */ - int found; - struct blame_entry *e; - struct origin *suspect = ent->suspect; - - if (0 < suspect->refcnt) - continue; - suspect->refcnt = -suspect->refcnt; /* Unmark */ - for (found = 0, e = sb->ent; e; e = e->next) { - if (e->suspect != suspect) - continue; - found++; - } - if (suspect->refcnt != found) { - fprintf(stderr, "%s in %s has refcnt %d, not %d\n", - ent->suspect->path, - sha1_to_hex(ent->suspect->commit->object.sha1), - ent->suspect->refcnt, found); - baa = 2; - } - } if (baa) { int opt = 0160; find_alignment(sb, &opt); diff --git a/builtin-branch.c b/builtin-branch.c index 504a981ad5..14d4b917e5 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -32,18 +32,18 @@ static unsigned char head_sha1[20]; static int branch_use_color = -1; static char branch_colors[][COLOR_MAXLEN] = { - "\033[m", /* reset */ - "", /* PLAIN (normal) */ - "\033[31m", /* REMOTE (red) */ - "", /* LOCAL (normal) */ - "\033[32m", /* CURRENT (green) */ + GIT_COLOR_RESET, + GIT_COLOR_NORMAL, /* PLAIN */ + GIT_COLOR_RED, /* REMOTE */ + GIT_COLOR_NORMAL, /* LOCAL */ + GIT_COLOR_GREEN, /* CURRENT */ }; enum color_branch { - COLOR_BRANCH_RESET = 0, - COLOR_BRANCH_PLAIN = 1, - COLOR_BRANCH_REMOTE = 2, - COLOR_BRANCH_LOCAL = 3, - COLOR_BRANCH_CURRENT = 4, + BRANCH_COLOR_RESET = 0, + BRANCH_COLOR_PLAIN = 1, + BRANCH_COLOR_REMOTE = 2, + BRANCH_COLOR_LOCAL = 3, + BRANCH_COLOR_CURRENT = 4, }; static enum merge_filter { @@ -56,15 +56,15 @@ static unsigned char merge_filter_ref[20]; static int parse_branch_color_slot(const char *var, int ofs) { if (!strcasecmp(var+ofs, "plain")) - return COLOR_BRANCH_PLAIN; + return BRANCH_COLOR_PLAIN; if (!strcasecmp(var+ofs, "reset")) - return COLOR_BRANCH_RESET; + return BRANCH_COLOR_RESET; if (!strcasecmp(var+ofs, "remote")) - return COLOR_BRANCH_REMOTE; + return BRANCH_COLOR_REMOTE; if (!strcasecmp(var+ofs, "local")) - return COLOR_BRANCH_LOCAL; + return BRANCH_COLOR_LOCAL; if (!strcasecmp(var+ofs, "current")) - return COLOR_BRANCH_CURRENT; + return BRANCH_COLOR_CURRENT; die("bad config variable '%s'", var); } @@ -188,7 +188,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) struct ref_item { char *name; - unsigned int kind; + char *dest; + unsigned int kind, len; struct commit *commit; }; @@ -200,22 +201,47 @@ struct ref_list { int kinds; }; +static char *resolve_symref(const char *src, const char *prefix) +{ + unsigned char sha1[20]; + int flag; + const char *dst, *cp; + + dst = resolve_ref(src, sha1, 0, &flag); + if (!(dst && (flag & REF_ISSYMREF))) + return NULL; + if (prefix && (cp = skip_prefix(dst, prefix))) + dst = cp; + return xstrdup(dst); +} + static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct ref_list *ref_list = (struct ref_list*)(cb_data); struct ref_item *newitem; struct commit *commit; - int kind; - int len; + int kind, i; + const char *prefix, *orig_refname = refname; + + static struct { + int kind; + const char *prefix; + int pfxlen; + } ref_kind[] = { + { REF_LOCAL_BRANCH, "refs/heads/", 11 }, + { REF_REMOTE_BRANCH, "refs/remotes/", 13 }, + }; /* Detect kind */ - if (!prefixcmp(refname, "refs/heads/")) { - kind = REF_LOCAL_BRANCH; - refname += 11; - } else if (!prefixcmp(refname, "refs/remotes/")) { - kind = REF_REMOTE_BRANCH; - refname += 13; - } else + for (i = 0; i < ARRAY_SIZE(ref_kind); i++) { + prefix = ref_kind[i].prefix; + if (strncmp(refname, prefix, ref_kind[i].pfxlen)) + continue; + kind = ref_kind[i].kind; + refname += ref_kind[i].pfxlen; + break; + } + if (ARRAY_SIZE(ref_kind) <= i) return 0; commit = lookup_commit_reference_gently(sha1, 1); @@ -246,9 +272,14 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, newitem->name = xstrdup(refname); newitem->kind = kind; newitem->commit = commit; - len = strlen(newitem->name); - if (len > ref_list->maxwidth) - ref_list->maxwidth = len; + newitem->len = strlen(refname); + newitem->dest = resolve_symref(orig_refname, prefix); + /* adjust for "remotes/" */ + if (newitem->kind == REF_REMOTE_BRANCH && + ref_list->kinds != REF_REMOTE_BRANCH) + newitem->len += 8; + if (newitem->len > ref_list->maxwidth) + ref_list->maxwidth = newitem->len; return 0; } @@ -257,8 +288,10 @@ static void free_ref_list(struct ref_list *ref_list) { int i; - for (i = 0; i < ref_list->index; i++) + for (i = 0; i < ref_list->index; i++) { free(ref_list->list[i].name); + free(ref_list->list[i].dest); + } free(ref_list->list); } @@ -299,34 +332,46 @@ static int matches_merge_filter(struct commit *commit) } static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, - int abbrev, int current) + int abbrev, int current, char *prefix) { char c; int color; struct commit *commit = item->commit; + struct strbuf out = STRBUF_INIT, name = STRBUF_INIT; if (!matches_merge_filter(commit)) return; switch (item->kind) { case REF_LOCAL_BRANCH: - color = COLOR_BRANCH_LOCAL; + color = BRANCH_COLOR_LOCAL; break; case REF_REMOTE_BRANCH: - color = COLOR_BRANCH_REMOTE; + color = BRANCH_COLOR_REMOTE; break; default: - color = COLOR_BRANCH_PLAIN; + color = BRANCH_COLOR_PLAIN; break; } c = ' '; if (current) { c = '*'; - color = COLOR_BRANCH_CURRENT; + color = BRANCH_COLOR_CURRENT; } - if (verbose) { + strbuf_addf(&name, "%s%s", prefix, item->name); + if (verbose) + strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color), + maxwidth, name.buf, + branch_get_color(BRANCH_COLOR_RESET)); + else + strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color), + name.buf, branch_get_color(BRANCH_COLOR_RESET)); + + if (item->dest) + strbuf_addf(&out, " -> %s", item->dest); + else if (verbose) { struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT; const char *sub = " **** invalid ref ****"; @@ -340,28 +385,25 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, if (item->kind == REF_LOCAL_BRANCH) fill_tracking_info(&stat, item->name); - printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color), - maxwidth, item->name, - branch_get_color(COLOR_BRANCH_RESET), - find_unique_abbrev(item->commit->object.sha1, abbrev), - stat.buf, sub); + strbuf_addf(&out, " %s %s%s", + find_unique_abbrev(item->commit->object.sha1, abbrev), + stat.buf, sub); strbuf_release(&stat); strbuf_release(&subject); - } else { - printf("%c %s%s%s\n", c, branch_get_color(color), item->name, - branch_get_color(COLOR_BRANCH_RESET)); } + printf("%s\n", out.buf); + strbuf_release(&name); + strbuf_release(&out); } static int calc_maxwidth(struct ref_list *refs) { - int i, l, w = 0; + int i, w = 0; for (i = 0; i < refs->index; i++) { if (!matches_merge_filter(refs->list[i].commit)) continue; - l = strlen(refs->list[i].name); - if (l > w) - w = l; + if (refs->list[i].len > w) + w = refs->list[i].len; } return w; } @@ -397,11 +439,13 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str is_descendant_of(head_commit, with_commit)) { struct ref_item item; item.name = xstrdup("(no branch)"); + item.len = strlen(item.name); item.kind = REF_LOCAL_BRANCH; + item.dest = NULL; item.commit = head_commit; - if (strlen(item.name) > ref_list.maxwidth) - ref_list.maxwidth = strlen(item.name); - print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1); + if (item.len > ref_list.maxwidth) + ref_list.maxwidth = item.len; + print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1, ""); free(item.name); } @@ -409,8 +453,11 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str int current = !detached && (ref_list.list[i].kind == REF_LOCAL_BRANCH) && !strcmp(ref_list.list[i].name, head); + char *prefix = (kinds != REF_REMOTE_BRANCH && + ref_list.list[i].kind == REF_REMOTE_BRANCH) + ? "remotes/" : ""; print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose, - abbrev, current); + abbrev, current, prefix); } free_ref_list(&ref_list); diff --git a/builtin-checkout.c b/builtin-checkout.c index 20b34ce6e1..c315f63398 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -295,6 +295,8 @@ static void show_local_changes(struct object *head) init_revisions(&rev, NULL); rev.abbrev = 0; rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS; + if (diff_setup_done(&rev.diffopt) < 0) + die("diff_setup_done failed"); add_pending_object(&rev, head, NULL); run_diff_index(&rev, 0); } diff --git a/builtin-clone.c b/builtin-clone.c index c338910b1c..92826cd14c 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -365,8 +365,6 @@ static void install_branch_config(const char *local, int cmd_clone(int argc, const char **argv, const char *prefix) { - int use_local_hardlinks = 1; - int use_separate_remote = 1; int is_bundle = 0; struct stat buf; const char *repo_name, *repo, *work_tree, *git_dir; @@ -388,9 +386,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (argc == 0) die("You must specify a repository to clone."); - if (option_no_hardlinks) - use_local_hardlinks = 0; - if (option_mirror) option_bare = 1; @@ -399,7 +394,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix) die("--bare and --origin %s options are incompatible.", option_origin); option_no_checkout = 1; - use_separate_remote = 0; } if (!option_origin) diff --git a/builtin-config.c b/builtin-config.c index f71016204b..d52a057444 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -3,7 +3,7 @@ #include "color.h" static const char git_config_set_usage[] = -"git config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int | --bool-or-int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]"; +"git config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int | --bool-or-int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty] | --edit | -e ]"; static char *key; static regex_t *key_regexp; @@ -27,7 +27,7 @@ static int show_all_config(const char *key_, const char *value_, void *cb) return 0; } -static int show_config(const char* key_, const char* value_, void *cb) +static int show_config(const char *key_, const char *value_, void *cb) { char value[256]; const char *vptr = value; @@ -74,7 +74,7 @@ static int show_config(const char* key_, const char* value_, void *cb) return 0; } -static int get_value(const char* key_, const char* regex_) +static int get_value(const char *key_, const char *regex_) { int ret = -1; char *tl; @@ -284,7 +284,7 @@ static int get_colorbool(int argc, const char **argv) int cmd_config(int argc, const char **argv, const char *prefix) { int nongit; - char* value; + char *value; const char *file = setup_git_directory_gently(&nongit); config_exclusive_filename = getenv(CONFIG_ENVIRONMENT); @@ -362,6 +362,14 @@ int cmd_config(int argc, const char **argv, const char *prefix) return get_color(argc-2, argv+2); } else if (!strcmp(argv[1], "--get-colorbool")) { return get_colorbool(argc-2, argv+2); + } else if (!strcmp(argv[1], "--edit") || !strcmp(argv[1], "-e")) { + if (argc != 2) + usage(git_config_set_usage); + git_config(git_default_config, NULL); + launch_editor(config_exclusive_filename ? + config_exclusive_filename : git_path("config"), + NULL, NULL); + return 0; } else break; argc--; diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 67fb80ec48..c2e5adc884 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -216,9 +216,8 @@ static int find_common(int fd[2], unsigned char *result_sha1, if (args.depth > 0) { char line[1024]; unsigned char sha1[20]; - int len; - while ((len = packet_read_line(fd[0], line, sizeof(line)))) { + while (packet_read_line(fd[0], line, sizeof(line))) { if (!prefixcmp(line, "shallow ")) { if (get_sha1_hex(line + 8, sha1)) die("invalid shallow line: %s", line); diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index df18f4070f..a7883690d7 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -256,8 +256,7 @@ static void shortlog(const char *name, unsigned char *sha1, int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) { int limit = 20, i = 0, pos = 0; - char line[1024]; - char *p = line, *sep = ""; + char *sep = ""; unsigned char head_sha1[20]; const char *current_branch; @@ -271,9 +270,8 @@ int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) { /* get a line */ while (pos < in->len) { int len; - char *newline; + char *newline, *p = in->buf + pos; - p = in->buf + pos; newline = strchr(p, '\n'); len = newline ? newline - p : strlen(p); pos += len + !!newline; diff --git a/builtin-log.c b/builtin-log.c index 2ae39afccd..8549028817 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -428,6 +428,8 @@ static const char *fmt_patch_suffix = ".patch"; static int numbered = 0; static int auto_number = 1; +static char *default_attach = NULL; + static char **extra_hdr; static int extra_hdr_nr; static int extra_hdr_alloc; @@ -488,6 +490,14 @@ static int git_format_config(const char *var, const char *value, void *cb) auto_number = auto_number && numbered; return 0; } + if (!strcmp(var, "format.attach")) { + if (value && *value) + default_attach = xstrdup(value); + else + default_attach = xstrdup(git_version_string); + return 0; + } + return git_log_config(var, value, cb); } @@ -787,6 +797,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.subject_prefix = fmt_patch_subject_prefix; + if (default_attach) { + rev.mime_boundary = default_attach; + rev.no_inline = 1; + } + /* * Parse the arguments before setup_revisions(), or something * like "git format-patch -o a123 HEAD^.." may fail; a123 is @@ -849,6 +864,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.mime_boundary = argv[i] + 9; rev.no_inline = 1; } + else if (!strcmp(argv[i], "--no-attach")) { + rev.mime_boundary = NULL; + rev.no_inline = 0; + } else if (!strcmp(argv[i], "--inline")) { rev.mime_boundary = git_version_string; rev.no_inline = 0; diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index cb51916fe3..bcefa52c69 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -488,9 +488,8 @@ static void write_pack_file(void) } else { char tmpname[PATH_MAX]; int fd; - snprintf(tmpname, sizeof(tmpname), - "%s/pack/tmp_pack_XXXXXX", get_object_directory()); - fd = xmkstemp(tmpname); + fd = odb_mkstemp(tmpname, sizeof(tmpname), + "pack/tmp_pack_XXXXXX"); pack_tmp_name = xstrdup(tmpname); f = sha1fd(fd, pack_tmp_name); } diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c index 849f1fe6f9..a970b39505 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -675,7 +675,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) setup_path(); if (!enter_repo(dir, 0)) - die("'%s': unable to chdir or not a git archive", dir); + die("'%s' does not appear to be a git repository", dir); if (is_repository_shallow()) die("attempt to push into a shallow repository"); diff --git a/builtin-remote.c b/builtin-remote.c index ac69d37c8a..e171096ece 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -484,9 +484,8 @@ static int mv(int argc, const char **argv) struct string_list_item *item = remote_branches.items + i; int flag = 0; unsigned char sha1[20]; - const char *symref; - symref = resolve_ref(item->string, sha1, 1, &flag); + resolve_ref(item->string, sha1, 1, &flag); if (!(flag & REF_ISSYMREF)) continue; if (delete_ref(item->string, NULL, REF_NODEREF)) diff --git a/builtin-rerere.c b/builtin-rerere.c index bd8fc77a7a..020af7377b 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -13,28 +13,17 @@ static const char git_rerere_usage[] = static int cutoff_noresolve = 15; static int cutoff_resolve = 60; -static const char *rr_path(const char *name, const char *file) -{ - return git_path("rr-cache/%s/%s", name, file); -} - static time_t rerere_created_at(const char *name) { struct stat st; - return stat(rr_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime; -} - -static int has_resolution(const char *name) -{ - struct stat st; - return !stat(rr_path(name, "postimage"), &st); + return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime; } static void unlink_rr_item(const char *name) { - unlink(rr_path(name, "thisimage")); - unlink(rr_path(name, "preimage")); - unlink(rr_path(name, "postimage")); + unlink(rerere_path(name, "thisimage")); + unlink(rerere_path(name, "preimage")); + unlink(rerere_path(name, "postimage")); rmdir(git_path("rr-cache/%s", name)); } @@ -65,7 +54,7 @@ static void garbage_collect(struct string_list *rr) then = rerere_created_at(e->d_name); if (!then) continue; - cutoff = (has_resolution(e->d_name) + cutoff = (has_rerere_resolution(e->d_name) ? cutoff_resolve : cutoff_noresolve); if (then < now - cutoff * 86400) string_list_append(e->d_name, &to_remove); @@ -124,7 +113,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix) if (!strcmp(argv[1], "clear")) { for (i = 0; i < merge_rr.nr; i++) { const char *name = (const char *)merge_rr.items[i].util; - if (!has_resolution(name)) + if (!has_rerere_resolution(name)) unlink_rr_item(name); } unlink(git_path("rr-cache/MERGE_RR")); @@ -137,7 +126,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix) for (i = 0; i < merge_rr.nr; i++) { const char *path = merge_rr.items[i].string; const char *name = (const char *)merge_rr.items[i].util; - diff_two(rr_path(name, "preimage"), path, path, path); + diff_two(rerere_path(name, "preimage"), path, path, path); } else usage(git_rerere_usage); diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 436afa45f5..40d5fcb6b0 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -574,6 +574,45 @@ static struct commit_list *find_bisection(struct commit_list *list, return best; } +static inline int log2i(int n) +{ + int log2 = 0; + + for (; n > 1; n >>= 1) + log2++; + + return log2; +} + +static inline int exp2i(int n) +{ + return 1 << n; +} + +/* + * Estimate the number of bisect steps left (after the current step) + * + * For any x between 0 included and 2^n excluded, the probability for + * n - 1 steps left looks like: + * + * P(2^n + x) == (2^n - x) / (2^n + x) + * + * and P(2^n + x) < 0.5 means 2^n < 3x + */ +static int estimate_bisect_steps(int all) +{ + int n, x, e; + + if (all < 3) + return 0; + + n = log2i(all); + e = exp2i(n); + x = all - e; + + return (e < 3 * x) ? n : n - 1; +} + int cmd_rev_list(int argc, const char **argv, const char *prefix) { struct commit_list *list; @@ -688,12 +727,14 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) "bisect_nr=%d\n" "bisect_good=%d\n" "bisect_bad=%d\n" - "bisect_all=%d\n", + "bisect_all=%d\n" + "bisect_steps=%d\n", hex, cnt - 1, all - reaches - 1, reaches - 1, - all); + all, + estimate_bisect_steps(all)); return 0; } } diff --git a/builtin-revert.c b/builtin-revert.c index d210150671..3f2614e1bb 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -376,6 +376,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) (write_cache(index_fd, active_cache, active_nr) || commit_locked_index(&index_lock))) die("%s: Unable to write new index file", me); + rollback_lock_file(&index_lock); if (!clean) { add_to_msg("\nConflicts:\n\n"); diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c index a9b02fa32f..0206b416cb 100644 --- a/builtin-upload-archive.c +++ b/builtin-upload-archive.c @@ -35,7 +35,7 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix) strcpy(buf, argv[1]); /* enter-repo smudges its argument */ if (!enter_repo(buf, 0)) - die("not a git archive"); + die("'%s' does not appear to be a git repository", buf); /* put received options in sent_argv[] */ sent_argc = 1; @@ -1,8 +1,6 @@ #include "cache.h" #include "color.h" -#define COLOR_RESET "\033[m" - int git_use_color_default = 0; static int parse_color(const char *name, int len) @@ -54,7 +52,7 @@ void color_parse_mem(const char *value, int value_len, const char *var, int bg = -2; if (!strncasecmp(value, "reset", len)) { - strcpy(dst, "\033[m"); + strcpy(dst, GIT_COLOR_RESET); return; } @@ -175,7 +173,7 @@ static int color_vfprintf(FILE *fp, const char *color, const char *fmt, r += fprintf(fp, "%s", color); r += vfprintf(fp, fmt, args); if (*color) - r += fprintf(fp, "%s", COLOR_RESET); + r += fprintf(fp, "%s", GIT_COLOR_RESET); if (trail) r += fprintf(fp, "%s", trail); return r; @@ -217,7 +215,7 @@ int color_fwrite_lines(FILE *fp, const char *color, char *p = memchr(buf, '\n', count); if (p != buf && (fputs(color, fp) < 0 || fwrite(buf, p ? p - buf : count, 1, fp) != 1 || - fputs(COLOR_RESET, fp) < 0)) + fputs(GIT_COLOR_RESET, fp) < 0)) return -1; if (!p) return 0; @@ -4,6 +4,16 @@ /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ #define COLOR_MAXLEN 24 +#define GIT_COLOR_NORMAL "" +#define GIT_COLOR_RESET "\033[m" +#define GIT_COLOR_BOLD "\033[1m" +#define GIT_COLOR_RED "\033[31m" +#define GIT_COLOR_GREEN "\033[32m" +#define GIT_COLOR_YELLOW "\033[33m" +#define GIT_COLOR_BLUE "\033[34m" +#define GIT_COLOR_CYAN "\033[36m" +#define GIT_COLOR_BG_RED "\033[41m" + /* * This variable stores the value of color.ui */ diff --git a/combine-diff.c b/combine-diff.c index bccc018ab2..b3b86aebcb 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -526,7 +526,6 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent, return; /* result deleted */ while (1) { - struct sline *sl = &sline[lno]; unsigned long hunk_end; unsigned long rlines; const char *hunk_comment = NULL; @@ -592,7 +591,7 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent, struct lline *ll; int j; unsigned long p_mask; - sl = &sline[lno++]; + struct sline *sl = &sline[lno++]; ll = (sl->flag & no_pre_delete) ? NULL : sl->lost_head; while (ll) { fputs(c_old, stdout); diff --git a/compat/mingw.c b/compat/mingw.c index 3dbe6a77ff..27bcf3fd6b 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -46,7 +46,8 @@ static int do_lstat(const char *file_name, struct stat *buf) buf->st_uid = 0; buf->st_nlink = 1; buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); - buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */ + buf->st_size = fdata.nFileSizeLow | + (((off_t)fdata.nFileSizeHigh)<<32); buf->st_dev = buf->st_rdev = 0; /* not used by Git */ buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); @@ -101,7 +102,7 @@ int mingw_fstat(int fd, struct stat *buf) } /* direct non-file handles to MS's fstat() */ if (GetFileType(fh) != FILE_TYPE_DISK) - return fstat(fd, buf); + return _fstati64(fd, buf); if (GetFileInformationByHandle(fh, &fdata)) { buf->st_ino = 0; @@ -109,7 +110,8 @@ int mingw_fstat(int fd, struct stat *buf) buf->st_uid = 0; buf->st_nlink = 1; buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); - buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */ + buf->st_size = fdata.nFileSizeLow | + (((off_t)fdata.nFileSizeHigh)<<32); buf->st_dev = buf->st_rdev = 0; /* not used by Git */ buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); diff --git a/compat/mingw.h b/compat/mingw.h index a255898801..6e24686442 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -163,11 +163,14 @@ int mingw_rename(const char*, const char*); /* Use mingw_lstat() instead of lstat()/stat() and * mingw_fstat() instead of fstat() on Windows. */ +#define off_t off64_t +#define stat _stati64 +#define lseek _lseeki64 int mingw_lstat(const char *file_name, struct stat *buf); int mingw_fstat(int fd, struct stat *buf); #define fstat mingw_fstat #define lstat mingw_lstat -#define stat(x,y) mingw_lstat(x,y) +#define _stati64(x,y) mingw_lstat(x,y) int mingw_utime(const char *file_name, const struct utimbuf *times); #define utime mingw_utime @@ -373,8 +373,6 @@ static void git_tcp_connect(int fd[2], char *host, int flags) static char *git_proxy_command; -static const char *rhost_name; -static int rhost_len; static int git_proxy_command_options(const char *var, const char *value, void *cb) @@ -383,6 +381,8 @@ static int git_proxy_command_options(const char *var, const char *value, const char *for_pos; int matchlen = -1; int hostlen; + const char *rhost_name = cb; + int rhost_len = strlen(rhost_name); if (git_proxy_command) return 0; @@ -426,11 +426,8 @@ static int git_proxy_command_options(const char *var, const char *value, static int git_use_proxy(const char *host) { - rhost_name = host; - rhost_len = strlen(host); git_proxy_command = getenv("GIT_PROXY_COMMAND"); - git_config(git_proxy_command_options, NULL); - rhost_name = NULL; + git_config(git_proxy_command_options, (void*)host); return (git_proxy_command && *git_proxy_command); } diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0a3092f646..271b911f7a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -62,7 +62,7 @@ esac __gitdir () { if [ -z "${1-}" ]; then - if [ -n "$__git_dir" ]; then + if [ -n "${__git_dir-}" ]; then echo "$__git_dir" elif [ -d .git ]; then echo .git @@ -80,68 +80,72 @@ __gitdir () # returns text to add to bash PS1 prompt (includes branch name) __git_ps1 () { - local g="$(git rev-parse --git-dir 2>/dev/null)" + local g="$(__gitdir)" if [ -n "$g" ]; then local r local b - if [ -d "$g/rebase-apply" ] - then - if test -f "$g/rebase-apply/rebasing" - then + if [ -d "$g/rebase-apply" ]; then + if [ -f "$g/rebase-apply/rebasing" ]; then r="|REBASE" - elif test -f "$g/rebase-apply/applying" - then + elif [ -f "$g/rebase-apply/applying" ]; then r="|AM" else r="|AM/REBASE" fi b="$(git symbolic-ref HEAD 2>/dev/null)" - elif [ -f "$g/rebase-merge/interactive" ] - then + elif [ -f "$g/rebase-merge/interactive" ]; then r="|REBASE-i" b="$(cat "$g/rebase-merge/head-name")" - elif [ -d "$g/rebase-merge" ] - then + elif [ -d "$g/rebase-merge" ]; then r="|REBASE-m" b="$(cat "$g/rebase-merge/head-name")" - elif [ -f "$g/MERGE_HEAD" ] - then + elif [ -f "$g/MERGE_HEAD" ]; then r="|MERGING" b="$(git symbolic-ref HEAD 2>/dev/null)" else - if [ -f "$g/BISECT_LOG" ] - then + if [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi - if ! b="$(git symbolic-ref HEAD 2>/dev/null)" - then - if ! b="$(git describe --exact-match HEAD 2>/dev/null)" - then - b="$(cut -c1-7 "$g/HEAD")..." + if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then + if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then + if [ -r "$g/HEAD" ]; then + b="$(cut -c1-7 "$g/HEAD")..." + fi fi fi fi local w local i + local c - if test -n "${GIT_PS1_SHOWDIRTYSTATE-}"; then - if test "$(git config --bool bash.showDirtyState)" != "false"; then - git diff --no-ext-diff --ignore-submodules \ - --quiet --exit-code || w="*" - if git rev-parse --quiet --verify HEAD >/dev/null; then - git diff-index --cached --quiet \ - --ignore-submodules HEAD -- || i="+" - else - i="#" + if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then + if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then + c="BARE:" + else + b="GIT_DIR!" + fi + elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then + if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then + if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then + git diff --no-ext-diff --ignore-submodules \ + --quiet --exit-code || w="*" + if git rev-parse --quiet --verify HEAD >/dev/null; then + git diff-index --cached --quiet \ + --ignore-submodules HEAD -- || i="+" + else + i="#" + fi fi fi fi - if [ -n "${1-}" ]; then - printf "$1" "${b##refs/heads/}$w$i$r" - else - printf " (%s)" "${b##refs/heads/}$w$i$r" + if [ -n "$b" ]; then + if [ -n "${1-}" ]; then + printf "$1" "$c${b##refs/heads/}$w$i$r" + else + printf " (%s)" "$c${b##refs/heads/}$w$i$r" + fi fi fi } @@ -299,7 +303,7 @@ __git_remotes () __git_merge_strategies () { - if [ -n "$__git_merge_strategylist" ]; then + if [ -n "${__git_merge_strategylist-}" ]; then echo "$__git_merge_strategylist" return fi @@ -383,9 +387,88 @@ __git_complete_revlist () esac } +__git_complete_remote_or_refspec () +{ + local cmd="${COMP_WORDS[1]}" + local cur="${COMP_WORDS[COMP_CWORD]}" + local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0 + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + --all|--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;; + -*) ;; + *) remote="$i"; break ;; + esac + c=$((++c)) + done + if [ -z "$remote" ]; then + __gitcomp "$(__git_remotes)" + return + fi + if [ $no_complete_refspec = 1 ]; then + COMPREPLY=() + return + fi + [ "$remote" = "." ] && remote= + case "$cur" in + *:*) + case "$COMP_WORDBREAKS" in + *:*) : great ;; + *) pfx="${cur%%:*}:" ;; + esac + cur="${cur#*:}" + lhs=0 + ;; + +*) + pfx="+" + cur="${cur#+}" + ;; + esac + case "$cmd" in + fetch) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur" + else + __gitcomp "$(__git_refs)" "$pfx" "$cur" + fi + ;; + pull) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur" + else + __gitcomp "$(__git_refs)" "$pfx" "$cur" + fi + ;; + push) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs)" "$pfx" "$cur" + else + __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur" + fi + ;; + esac +} + +__git_complete_strategy () +{ + case "${COMP_WORDS[COMP_CWORD-1]}" in + -s|--strategy) + __gitcomp "$(__git_merge_strategies)" + return 0 + esac + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --strategy=*) + __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" + return 0 + ;; + esac + return 1 +} + __git_all_commands () { - if [ -n "$__git_all_commandlist" ]; then + if [ -n "${__git_all_commandlist-}" ]; then echo "$__git_all_commandlist" return fi @@ -403,7 +486,7 @@ __git_all_commandlist="$(__git_all_commands 2>/dev/null)" __git_porcelain_commands () { - if [ -n "$__git_porcelain_commandlist" ]; then + if [ -n "${__git_porcelain_commandlist-}" ]; then echo "$__git_porcelain_commandlist" return fi @@ -826,27 +909,21 @@ _git_diff () __git_complete_file } +__git_fetch_options=" + --quiet --verbose --append --upload-pack --force --keep --depth= + --tags --no-tags +" + _git_fetch () { local cur="${COMP_WORDS[COMP_CWORD]}" - - if [ "$COMP_CWORD" = 2 ]; then - __gitcomp "$(__git_remotes)" - else - case "$cur" in - *:*) - local pfx="" - case "$COMP_WORDBREAKS" in - *:*) : great ;; - *) pfx="${cur%%:*}:" ;; - esac - __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}" - ;; - *) - __gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")" - ;; - esac - fi + case "$cur" in + --*) + __gitcomp "$__git_fetch_options" + return + ;; + esac + __git_complete_remote_or_refspec } _git_format_patch () @@ -1014,6 +1091,11 @@ _git_log () " "" "${cur##--pretty=}" return ;; + --format=*) + __gitcomp "$__git_log_pretty_formats + " "" "${cur##--format=}" + return + ;; --date=*) __gitcomp " relative iso8601 rfc2822 short local default @@ -1029,7 +1111,7 @@ _git_log () --follow --abbrev-commit --abbrev= --relative-date --date= - --pretty= + --pretty= --format= --oneline --cherry-pick --graph --decorate @@ -1045,24 +1127,19 @@ _git_log () __git_complete_revlist } +__git_merge_options=" + --no-commit --no-stat --log --no-log --squash --strategy + --commit --stat --no-squash --ff --no-ff +" + _git_merge () { + __git_complete_strategy && return + local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[COMP_CWORD-1]}" in - -s|--strategy) - __gitcomp "$(__git_merge_strategies)" - return - esac case "$cur" in - --strategy=*) - __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" - return - ;; --*) - __gitcomp " - --no-commit --no-stat --log --no-log --squash --strategy - --commit --stat --no-squash --ff --no-ff - " + __gitcomp "$__git_merge_options" return esac __gitcomp "$(__git_refs)" @@ -1111,40 +1188,44 @@ _git_name_rev () _git_pull () { - local cur="${COMP_WORDS[COMP_CWORD]}" + __git_complete_strategy && return - if [ "$COMP_CWORD" = 2 ]; then - __gitcomp "$(__git_remotes)" - else - __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" - fi + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --rebase --no-rebase + $__git_merge_options + $__git_fetch_options + " + return + ;; + esac + __git_complete_remote_or_refspec } _git_push () { local cur="${COMP_WORDS[COMP_CWORD]}" - - if [ "$COMP_CWORD" = 2 ]; then + case "${COMP_WORDS[COMP_CWORD-1]}" in + --repo) __gitcomp "$(__git_remotes)" - else - case "$cur" in - *:*) - local pfx="" - case "$COMP_WORDBREAKS" in - *:*) : great ;; - *) pfx="${cur%%:*}:" ;; - esac - - __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}" - ;; - +*) - __gitcomp "$(__git_refs)" + "${cur#+}" - ;; - *) - __gitcomp "$(__git_refs)" - ;; - esac - fi + return + esac + case "$cur" in + --repo=*) + __gitcomp "$(__git_remotes)" "" "${cur##--repo=}" + return + ;; + --*) + __gitcomp " + --all --mirror --tags --dry-run --force --verbose + --receive-pack= --repo= + " + return + ;; + esac + __git_complete_remote_or_refspec } _git_rebase () @@ -1154,16 +1235,8 @@ _git_rebase () __gitcomp "--continue --skip --abort" return fi - case "${COMP_WORDS[COMP_CWORD-1]}" in - -s|--strategy) - __gitcomp "$(__git_merge_strategies)" - return - esac + __git_complete_strategy && return case "$cur" in - --strategy=*) - __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" - return - ;; --*) __gitcomp "--onto --merge --strategy --interactive" return @@ -1541,8 +1614,13 @@ _git_show () " "" "${cur##--pretty=}" return ;; + --format=*) + __gitcomp "$__git_log_pretty_formats + " "" "${cur##--format=}" + return + ;; --*) - __gitcomp "--pretty= + __gitcomp "--pretty= --format= $__git_diff_common_options " return @@ -1841,7 +1919,7 @@ _gitk () __git_has_doubledash && return local cur="${COMP_WORDS[COMP_CWORD]}" - local g="$(git rev-parse --git-dir 2>/dev/null)" + local g="$(__gitdir)" local merge="" if [ -f $g/MERGE_HEAD ]; then merge="--merge" diff --git a/contrib/examples/git-svnimport.perl b/contrib/examples/git-svnimport.perl index a13bb6afec..4576c4a862 100755 --- a/contrib/examples/git-svnimport.perl +++ b/contrib/examples/git-svnimport.perl @@ -287,9 +287,9 @@ my $last_rev = ""; my $last_branch; my $current_rev = $opt_s || 1; unless(-d $git_dir) { - system("git-init"); + system("git init"); die "Cannot init the GIT db at $git_tree: $?\n" if $?; - system("git-read-tree"); + system("git read-tree"); die "Cannot init an empty tree: $?\n" if $?; $last_branch = $opt_o; @@ -303,7 +303,7 @@ unless(-d $git_dir) { -f "$git_dir/svn2git" or die "'$git_dir/svn2git' does not exist.\n". "You need that file for incremental imports.\n"; - open(F, "git-symbolic-ref HEAD |") or + open(F, "git symbolic-ref HEAD |") or die "Cannot run git-symbolic-ref: $!\n"; chomp ($last_branch = <F>); $last_branch = basename($last_branch); @@ -331,7 +331,7 @@ EOM "$git_dir/refs/heads/$opt_o") == 0; # populate index - system('git-read-tree', $last_rev); + system('git', 'read-tree', $last_rev); die "read-tree failed: $?\n" if $?; # Get the last import timestamps @@ -399,7 +399,7 @@ sub get_file($$$) { my $pid = open(my $F, '-|'); die $! unless defined $pid; if (!$pid) { - exec("git-hash-object", "-w", $name) + exec("git", "hash-object", "-w", $name) or die "Cannot create object: $!\n"; } my $sha = <$F>; @@ -423,7 +423,7 @@ sub get_ignore($$$$$) { my $pid = open(my $F, '-|'); die $! unless defined $pid; if (!$pid) { - exec("git-hash-object", "-w", $name) + exec("git", "hash-object", "-w", $name) or die "Cannot create object: $!\n"; } my $sha = <$F>; @@ -547,7 +547,7 @@ sub copy_path($$$$$$$$) { my $pid = open my $f,'-|'; die $! unless defined $pid; if (!$pid) { - exec("git-ls-tree","-r","-z",$gitrev,$srcpath) + exec("git","ls-tree","-r","-z",$gitrev,$srcpath) or die $!; } local $/ = "\0"; @@ -634,7 +634,7 @@ sub commit { my $rev; if($revision > $opt_s and defined $parent) { - open(H,'-|',"git-rev-parse","--verify",$parent); + open(H,'-|',"git","rev-parse","--verify",$parent); $rev = <H>; close(H) or do { print STDERR "$revision: cannot find commit '$parent'!\n"; @@ -671,7 +671,7 @@ sub commit { unlink($git_index); } elsif ($rev ne $last_rev) { print "Switching from $last_rev to $rev ($branch)\n" if $opt_v; - system("git-read-tree", $rev); + system("git", "read-tree", $rev); die "read-tree failed for $rev: $?\n" if $?; $last_rev = $rev; } @@ -740,7 +740,7 @@ sub commit { my $pid = open my $F, "-|"; die "$!" unless defined $pid; if (!$pid) { - exec("git-ls-files", "-z", @o1) or die $!; + exec("git", "ls-files", "-z", @o1) or die $!; } @o1 = (); local $/ = "\0"; @@ -758,7 +758,7 @@ sub commit { @o2 = @o1; @o1 = (); } - system("git-update-index","--force-remove","--",@o2); + system("git","update-index","--force-remove","--",@o2); die "Cannot remove files: $?\n" if $?; } } @@ -770,7 +770,7 @@ sub commit { @n2 = @new; @new = (); } - system("git-update-index","--add", + system("git","update-index","--add", (map { ('--cacheinfo', @$_) } @n2)); die "Cannot add files: $?\n" if $?; } @@ -778,7 +778,7 @@ sub commit { my $pid = open(C,"-|"); die "Cannot fork: $!" unless defined $pid; unless($pid) { - exec("git-write-tree"); + exec("git","write-tree"); die "Cannot exec git-write-tree: $!\n"; } chomp(my $tree = <C>); @@ -830,7 +830,7 @@ sub commit { "GIT_COMMITTER_NAME=$committer_name", "GIT_COMMITTER_EMAIL=$committer_email", "GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), - "git-commit-tree", $tree,@par); + "git", "commit-tree", $tree,@par); die "Cannot exec git-commit-tree: $!\n"; } $pw->writer(); @@ -874,7 +874,7 @@ sub commit { $dest =~ tr/_/\./ if $opt_u; - system('git-tag', '-f', $dest, $cid) == 0 + system('git', 'tag', '-f', $dest, $cid) == 0 or die "Cannot create tag $dest: $!\n"; print "Created tag '$dest' on '$branch'\n" if $opt_v; @@ -937,7 +937,7 @@ while ($to_rev < $opt_l) { my $pid = fork(); die "Fork: $!\n" unless defined $pid; unless($pid) { - exec("git-repack", "-d") + exec("git", "repack", "-d") or die "Cannot repack: $!\n"; } waitpid($pid, 0); @@ -958,7 +958,7 @@ if($orig_branch) { system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") if $forward_master; unless ($opt_i) { - system('git-read-tree', '-m', '-u', 'SVN2GIT_HEAD', 'HEAD'); + system('git', 'read-tree', '-m', '-u', 'SVN2GIT_HEAD', 'HEAD'); die "read-tree failed: $?\n" if $?; } } else { @@ -966,7 +966,7 @@ if($orig_branch) { print "DONE; creating $orig_branch branch\n" if $opt_v and (not defined $opt_l or $opt_l > 0); system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") unless -f "$git_dir/refs/heads/master"; - system('git-update-ref', 'HEAD', "$orig_branch"); + system('git', 'update-ref', 'HEAD', "$orig_branch"); unless ($opt_i) { system('git checkout'); die "checkout failed: $?\n" if $?; diff --git a/contrib/examples/git-svnimport.txt b/contrib/examples/git-svnimport.txt index 71aad8b45b..3bb871e42f 100644 --- a/contrib/examples/git-svnimport.txt +++ b/contrib/examples/git-svnimport.txt @@ -114,9 +114,9 @@ due to SVN memory leaks. (These have been worked around.) -R <repack_each_revs>:: Specify how often git repository should be repacked. + -The default value is 1000. git-svnimport will do import in chunks of 1000 -revisions, after each chunk git repository will be repacked. To disable -this behavior specify some big value here which is mote than number of +The default value is 1000. git-svnimport will do imports in chunks of 1000 +revisions, after each chunk the git repository will be repacked. To disable +this behavior specify some large value here which is greater than the number of revisions to import. -P <path_from_trunk>:: diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a85a7b2a58..342529db30 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -442,13 +442,14 @@ def p4ChangesForPaths(depotPaths, changeRange): output = p4_read_pipe_lines("changes " + ' '.join (["%s...%s" % (p, changeRange) for p in depotPaths])) - changes = [] + changes = {} for line in output: - changeNum = line.split(" ")[1] - changes.append(int(changeNum)) + changeNum = int(line.split(" ")[1]) + changes[changeNum] = True - changes.sort() - return changes + changelist = changes.keys() + changelist.sort() + return changelist class Command: def __init__(self): @@ -1141,7 +1142,7 @@ class P4Sync(Command): s = '' for (key, val) in self.users.items(): - s += "%s\t%s\n" % (key, val) + s += "%s\t%s\n" % (key.expandtabs(1), val.expandtabs(1)) open(self.getUserCacheFilename(), "wb").write(s) self.userMapFromPerforceServer = True @@ -229,7 +229,7 @@ static char *path_ok(char *directory) } if (!path) { - logerror("'%s': unable to chdir or not a git archive", dir); + logerror("'%s' does not appear to be a git repository", dir); return NULL; } @@ -133,7 +133,25 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode) snprintf(timebuf, sizeof(timebuf), "%lu months ago", (diff + 15) / 30); return timebuf; } - /* Else fall back on absolute format.. */ + /* Give years and months for 5 years or so */ + if (diff < 1825) { + unsigned long years = (diff + 183) / 365; + unsigned long months = (diff % 365 + 15) / 30; + int n; + n = snprintf(timebuf, sizeof(timebuf), "%lu year%s", + years, (years > 1 ? "s" : "")); + if (months) + snprintf(timebuf + n, sizeof(timebuf) - n, + ", %lu month%s ago", + months, (months > 1 ? "s" : "")); + else + snprintf(timebuf + n, sizeof(timebuf) - n, + " ago"); + return timebuf; + } + /* Otherwise, just years. Centuries is probably overkill. */ + snprintf(timebuf, sizeof(timebuf), "%lu years ago", (diff + 183) / 365); + return timebuf; } if (mode == DATE_LOCAL) diff --git a/diff-no-index.c b/diff-no-index.c index 0a14268ba9..598687b50a 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -38,6 +38,10 @@ static int get_mode(const char *path, int *mode) if (!path || !strcmp(path, "/dev/null")) *mode = 0; +#ifdef _WIN32 + else if (!strcasecmp(path, "nul")) + *mode = 0; +#endif else if (!strcmp(path, "-")) *mode = create_ce_mode(0666); else if (lstat(path, &st)) @@ -30,14 +30,14 @@ int diff_auto_refresh_index = 1; static int diff_mnemonic_prefix; static char diff_colors[][COLOR_MAXLEN] = { - "\033[m", /* reset */ - "", /* PLAIN (normal) */ - "\033[1m", /* METAINFO (bold) */ - "\033[36m", /* FRAGINFO (cyan) */ - "\033[31m", /* OLD (red) */ - "\033[32m", /* NEW (green) */ - "\033[33m", /* COMMIT (yellow) */ - "\033[41m", /* WHITESPACE (red background) */ + GIT_COLOR_RESET, + GIT_COLOR_NORMAL, /* PLAIN */ + GIT_COLOR_BOLD, /* METAINFO */ + GIT_COLOR_CYAN, /* FRAGINFO */ + GIT_COLOR_RED, /* OLD */ + GIT_COLOR_GREEN, /* NEW */ + GIT_COLOR_YELLOW, /* COMMIT */ + GIT_COLOR_BG_RED, /* WHITESPACE */ }; static void diff_filespec_load_driver(struct diff_filespec *one); @@ -875,7 +875,7 @@ static void fill_print_name(struct diffstat_file *file) static void show_stats(struct diffstat_t* data, struct diff_options *options) { - int i, len, add, del, total, adds = 0, dels = 0; + int i, len, add, del, adds = 0, dels = 0; int max_change = 0, max_len = 0; int total_files = data->nr; int width, name_width; @@ -978,14 +978,12 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) */ add = added; del = deleted; - total = add + del; adds += add; dels += del; if (width <= max_change) { add = scale_linear(add, width, max_change); del = scale_linear(del, width, max_change); - total = add + del; } show_name(options->file, prefix, name, len, reset, set); fprintf(options->file, "%5d%s", added + deleted, @@ -2567,13 +2565,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) /* xdiff options */ else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space")) - options->xdl_opts |= XDF_IGNORE_WHITESPACE; + DIFF_XDL_SET(options, IGNORE_WHITESPACE); else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change")) - options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE; + DIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE); else if (!strcmp(arg, "--ignore-space-at-eol")) - options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL; + DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL); else if (!strcmp(arg, "--patience")) - options->xdl_opts |= XDF_PATIENCE_DIFF; + DIFF_XDL_SET(options, PATIENCE_DIFF); /* flags options */ else if (!strcmp(arg, "--binary")) { @@ -2594,10 +2592,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_SET(options, COLOR_DIFF); else if (!strcmp(arg, "--no-color")) DIFF_OPT_CLR(options, COLOR_DIFF); - else if (!strcmp(arg, "--color-words")) - options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS; + else if (!strcmp(arg, "--color-words")) { + DIFF_OPT_SET(options, COLOR_DIFF); + DIFF_OPT_SET(options, COLOR_DIFF_WORDS); + } else if (!prefixcmp(arg, "--color-words=")) { - options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS; + DIFF_OPT_SET(options, COLOR_DIFF); + DIFF_OPT_SET(options, COLOR_DIFF_WORDS); options->word_regex = arg + 14; } else if (!strcmp(arg, "--exit-code")) @@ -69,6 +69,9 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) #define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag) #define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag) +#define DIFF_XDL_TST(opts, flag) ((opts)->xdl_opts & XDF_##flag) +#define DIFF_XDL_SET(opts, flag) ((opts)->xdl_opts |= XDF_##flag) +#define DIFF_XDL_CLR(opts, flag) ((opts)->xdl_opts &= ~XDF_##flag) struct diff_options { const char *filter; diff --git a/diffcore-break.c b/diffcore-break.c index 31cdcfe8bc..d7097bb576 100644 --- a/diffcore-break.c +++ b/diffcore-break.c @@ -45,7 +45,7 @@ static int should_break(struct diff_filespec *src, * The value we return is 1 if we want the pair to be broken, * or 0 if we do not. */ - unsigned long delta_size, base_size, max_size; + unsigned long delta_size, max_size; unsigned long src_copied, literal_added, src_removed; *merge_score_p = 0; /* assume no deletion --- "do not break" @@ -64,7 +64,6 @@ static int should_break(struct diff_filespec *src, if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0)) return 0; /* error but caught downstream */ - base_size = ((src->size < dst->size) ? src->size : dst->size); max_size = ((src->size > dst->size) ? src->size : dst->size); if (max_size < MINIMUM_BREAK_SIZE) return 0; /* we do not break too small filepair */ @@ -137,7 +137,7 @@ int match_pathspec(const char **pathspec, const char *name, int namelen, static int no_wildcard(const char *string) { - return string[strcspn(string, "*?[{")] == '\0'; + return string[strcspn(string, "*?[{\\")] == '\0'; } void add_exclude(const char *string, const char *base, diff --git a/fast-import.c b/fast-import.c index 3ef3413e69..beeac0d004 100644 --- a/fast-import.c +++ b/fast-import.c @@ -817,9 +817,8 @@ static void start_packfile(void) struct pack_header hdr; int pack_fd; - snprintf(tmpfile, sizeof(tmpfile), - "%s/pack/tmp_pack_XXXXXX", get_object_directory()); - pack_fd = xmkstemp(tmpfile); + pack_fd = odb_mkstemp(tmpfile, sizeof(tmpfile), + "pack/tmp_pack_XXXXXX"); p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2); strcpy(p->pack_name, tmpfile); p->pack_fd = pack_fd; @@ -879,9 +878,8 @@ static char *create_index(void) c = next; } - snprintf(tmpfile, sizeof(tmpfile), - "%s/pack/tmp_idx_XXXXXX", get_object_directory()); - idx_fd = xmkstemp(tmpfile); + idx_fd = odb_mkstemp(tmpfile, sizeof(tmpfile), + "pack/tmp_idx_XXXXXX"); f = sha1fd(idx_fd, tmpfile); sha1write(f, array, 256 * sizeof(int)); git_SHA1_Init(&ctx); @@ -907,9 +905,7 @@ static char *keep_pack(char *curr_index_name) chmod(pack_data->pack_name, 0444); chmod(curr_index_name, 0444); - snprintf(name, sizeof(name), "%s/pack/pack-%s.keep", - get_object_directory(), sha1_to_hex(pack_data->sha1)); - keep_fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600); + keep_fd = odb_pack_keep(name, sizeof(name), pack_data->sha1); if (keep_fd < 0) die("cannot create keep file"); write_or_die(keep_fd, keep_msg, strlen(keep_msg)); @@ -1749,21 +1745,19 @@ static void parse_data(struct strbuf *sb) static int validate_raw_date(const char *src, char *result, int maxlen) { const char *orig_src = src; - char *endp, sign; - unsigned long date; + char *endp; errno = 0; - date = strtoul(src, &endp, 10); + strtoul(src, &endp, 10); if (errno || endp == src || *endp != ' ') return -1; src = endp + 1; if (*src != '-' && *src != '+') return -1; - sign = *src; - date = strtoul(src + 1, &endp, 10); + strtoul(src + 1, &endp, 10); if (errno || endp == src || *endp || (endp - orig_src) >= maxlen) return -1; @@ -148,20 +148,17 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) struct tree_desc desc; unsigned o_mode; const char *o_name; - const unsigned char *o_sha1; init_tree_desc(&desc, item->buffer, item->size); o_mode = 0; o_name = NULL; - o_sha1 = NULL; while (desc.size) { unsigned mode; const char *name; - const unsigned char *sha1; - sha1 = tree_entry_extract(&desc, &name, &mode); + tree_entry_extract(&desc, &name, &mode); if (strchr(name, '/')) has_full_path = 1; @@ -207,7 +204,6 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func) o_mode = mode; o_name = name; - o_sha1 = sha1; } retval = 0; diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 5f129a4203..def062a9e2 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -3,6 +3,8 @@ use strict; use Git; +binmode(STDOUT, ":raw"); + my $repo = Git->repository(); my $menu_use_color = $repo->get_colorbool('color.interactive'); @@ -91,6 +93,47 @@ if (!defined $GIT_DIR) { } chomp($GIT_DIR); +my %cquote_map = ( + "b" => chr(8), + "t" => chr(9), + "n" => chr(10), + "v" => chr(11), + "f" => chr(12), + "r" => chr(13), + "\\" => "\\", + "\042" => "\042", +); + +sub unquote_path { + local ($_) = @_; + my ($retval, $remainder); + if (!/^\042(.*)\042$/) { + return $_; + } + ($_, $retval) = ($1, ""); + while (/^([^\\]*)\\(.*)$/) { + $remainder = $2; + $retval .= $1; + for ($remainder) { + if (/^([0-3][0-7][0-7])(.*)$/) { + $retval .= chr(oct($1)); + $_ = $2; + last; + } + if (/^([\\\042btnvfr])(.*)$/) { + $retval .= $cquote_map{$1}; + $_ = $2; + last; + } + # This is malformed -- just return it as-is for now. + return $_[0]; + } + $_ = $remainder; + } + $retval .= $_; + return $retval; +} + sub refresh { my $fh; open $fh, 'git update-index --refresh |' @@ -104,7 +147,7 @@ sub refresh { sub list_untracked { map { chomp $_; - $_; + unquote_path($_); } run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV); } @@ -141,7 +184,8 @@ sub list_modified { if (@ARGV) { @tracked = map { - chomp $_; $_; + chomp $_; + unquote_path($_); } run_cmd_pipe(qw(git ls-files --exclude-standard --), @ARGV); return if (!@tracked); } @@ -153,6 +197,7 @@ sub list_modified { if (($add, $del, $file) = /^([-\d]+) ([-\d]+) (.*)/) { my ($change, $bin); + $file = unquote_path($file); if ($add eq '-' && $del eq '-') { $change = 'binary'; $bin = 1; @@ -168,6 +213,7 @@ sub list_modified { } elsif (($adddel, $file) = /^ (create|delete) mode [0-7]+ (.*)$/) { + $file = unquote_path($file); $data{$file}{INDEX_ADDDEL} = $adddel; } } @@ -175,6 +221,7 @@ sub list_modified { for (run_cmd_pipe(qw(git diff-files --numstat --summary --), @tracked)) { if (($add, $del, $file) = /^([-\d]+) ([-\d]+) (.*)/) { + $file = unquote_path($file); if (!exists $data{$file}) { $data{$file} = +{ INDEX => 'unchanged', @@ -196,6 +243,7 @@ sub list_modified { } elsif (($adddel, $file) = /^ (create|delete) mode [0-7]+ (.*)$/) { + $file = unquote_path($file); $data{$file}{FILE_ADDDEL} = $adddel; } } @@ -302,7 +350,8 @@ sub find_unique_prefixes { } %search = %{$search{$letter}}; } - if ($soft_limit && $j + 1 > $soft_limit) { + if (ord($letters[0]) > 127 || + ($soft_limit && $j + 1 > $soft_limit)) { $prefix = undef; $remainder = $ret; } @@ -753,6 +802,10 @@ EOF || $ENV{VISUAL} || $ENV{EDITOR} || "vi"; system('sh', '-c', $editor.' "$@"', $editor, $hunkfile); + if ($? != 0) { + return undef; + } + open $fh, '<', $hunkfile or die "failed to open hunk edit file for reading: " . $!; my @newtext = grep { !/^#/ } <$fh>; @@ -221,6 +221,9 @@ then resume=yes case "$skip,$abort" in + t,t) + die "Please make up your mind. --skip or --abort?" + ;; t,) git rerere clear git read-tree --reset -u HEAD HEAD @@ -229,12 +232,19 @@ then git update-ref ORIG_HEAD $orig_head ;; ,t) + if test -f "$dotest/rebasing" + then + exec git rebase --abort + fi git rerere clear - git read-tree --reset -u HEAD ORIG_HEAD - git reset ORIG_HEAD + test -f "$dotest/dirtyindex" || { + git read-tree --reset -u HEAD ORIG_HEAD + git reset ORIG_HEAD + } rm -fr "$dotest" exit ;; esac + rm -f "$dotest/dirtyindex" else # Make sure we are not given --skip, --resolved, nor --abort test "$skip$resolved$abort" = "" || @@ -287,7 +297,11 @@ fi case "$resolved" in '') files=$(git diff-index --cached --name-only HEAD --) || exit - test "$files" && die "Dirty index: cannot apply patches (dirty: $files)" + if test "$files" + then + : >"$dotest/dirtyindex" + die "Dirty index: cannot apply patches (dirty: $files)" + fi esac if test "$(cat "$dotest/utf8")" = t diff --git a/git-bisect.sh b/git-bisect.sh index 85db4ba400..e313bdea70 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -284,62 +284,74 @@ filter_skipped() { _skip="$2" if [ -z "$_skip" ]; then - eval "$_eval" + eval "$_eval" | { + while read line + do + echo "$line &&" + done + echo ':' + } return fi # Let's parse the output of: # "git rev-list --bisect-vars --bisect-all ..." - eval "$_eval" | while read hash line - do - case "$VARS,$FOUND,$TRIED,$hash" in - # We display some vars. - 1,*,*,*) echo "$hash $line" ;; - - # Split line. - ,*,*,---*) ;; - - # We had nothing to search. + eval "$_eval" | { + VARS= FOUND= TRIED= + while read hash line + do + case "$VARS,$FOUND,$TRIED,$hash" in + 1,*,*,*) + # "bisect_foo=bar" read from rev-list output. + echo "$hash &&" + ;; + ,*,*,---*) + # Separator + ;; ,,,bisect_rev*) - echo "bisect_rev=" + # We had nothing to search. + echo "bisect_rev= &&" VARS=1 ;; - - # We did not find a good bisect rev. - # This should happen only if the "bad" - # commit is also a "skip" commit. ,,*,bisect_rev*) - echo "bisect_rev=$TRIED" + # We did not find a good bisect rev. + # This should happen only if the "bad" + # commit is also a "skip" commit. + echo "bisect_rev='$TRIED' &&" VARS=1 ;; - - # We are searching. ,,*,*) + # We are searching. TRIED="${TRIED:+$TRIED|}$hash" case "$_skip" in *$hash*) ;; *) - echo "bisect_rev=$hash" - echo "bisect_tried=\"$TRIED\"" + echo "bisect_rev=$hash &&" + echo "bisect_tried='$TRIED' &&" FOUND=1 ;; esac ;; - - # We have already found a rev to be tested. - ,1,*,bisect_rev*) VARS=1 ;; - ,1,*,*) ;; - - # ??? - *) die "filter_skipped error " \ - "VARS: '$VARS' " \ - "FOUND: '$FOUND' " \ - "TRIED: '$TRIED' " \ - "hash: '$hash' " \ - "line: '$line'" - ;; - esac - done + ,1,*,bisect_rev*) + # We have already found a rev to be tested. + VARS=1 + ;; + ,1,*,*) + ;; + *) + # Unexpected input + echo "die 'filter_skipped error'" + die "filter_skipped error " \ + "VARS: '$VARS' " \ + "FOUND: '$FOUND' " \ + "TRIED: '$TRIED' " \ + "hash: '$hash' " \ + "line: '$line'" + ;; + esac + done + echo ':' + } } exit_if_skipped_commits () { @@ -500,7 +512,7 @@ bisect_next() { # commit is also a "skip" commit (see above). exit_if_skipped_commits "$bisect_rev" - bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this" + bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this (roughly $bisect_steps steps)" } bisect_visualize() { diff --git a/git-compat-util.h b/git-compat-util.h index 079cbe9440..878d83dd08 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -303,6 +303,8 @@ extern ssize_t xwrite(int fd, const void *buf, size_t len); extern int xdup(int fd); extern FILE *xfdopen(int fd, const char *mode); extern int xmkstemp(char *template); +extern int odb_mkstemp(char *template, size_t limit, const char *pattern); +extern int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1); static inline size_t xsize_t(off_t len) { @@ -317,6 +319,7 @@ static inline int has_extension(const char *filename, const char *ext) } /* Sane ctype - no locale, and works with signed chars */ +#undef isascii #undef isspace #undef isdigit #undef isalpha @@ -330,6 +333,7 @@ extern unsigned char sane_ctype[256]; #define GIT_GLOB_SPECIAL 0x08 #define GIT_REGEX_SPECIAL 0x10 #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0) +#define isascii(x) (((x) & ~0x7f) == 0) #define isspace(x) sane_istest(x,GIT_SPACE) #define isdigit(x) sane_istest(x,GIT_DIGIT) #define isalpha(x) sane_istest(x,GIT_ALPHA) diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 9a09ba1382..20f6f51750 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -232,7 +232,9 @@ while read sha1 type name do case "$force,$name" in ,$orig_namespace*) - die "Namespace $orig_namespace not empty" + die "Cannot create a new backup. +A previous backup already exists in $orig_namespace +Force overwriting the backup with -f" ;; t,$orig_namespace*) git update-ref -d "$name" $sha1 diff --git a/git-instaweb.sh b/git-instaweb.sh index 0843372b57..5f4419b69b 100755 --- a/git-instaweb.sh +++ b/git-instaweb.sh @@ -49,7 +49,7 @@ resolve_full_httpd () { esac httpd_only="$(echo $httpd | cut -f1 -d' ')" - if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac + if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null 2>&1;; esac then full_httpd=$httpd else @@ -179,11 +179,74 @@ lighttpd_conf () { cat > "$conf" <<EOF server.document-root = "$fqgitdir/gitweb" server.port = $port -server.modules = ( "mod_cgi" ) +server.modules = ( "mod_setenv", "mod_cgi" ) server.indexfiles = ( "gitweb.cgi" ) server.pid-file = "$fqgitdir/pid" +server.errorlog = "$fqgitdir/gitweb/error.log" + +# to enable, add "mod_access", "mod_accesslog" to server.modules +# variable above and uncomment this +#accesslog.filename = "$fqgitdir/gitweb/access.log" + +setenv.add-environment = ( "PATH" => "/usr/local/bin:/usr/bin:/bin" ) + cgi.assign = ( ".cgi" => "" ) -mimetype.assign = ( ".css" => "text/css" ) + +# mimetype mapping +mimetype.assign = ( + ".pdf" => "application/pdf", + ".sig" => "application/pgp-signature", + ".spl" => "application/futuresplash", + ".class" => "application/octet-stream", + ".ps" => "application/postscript", + ".torrent" => "application/x-bittorrent", + ".dvi" => "application/x-dvi", + ".gz" => "application/x-gzip", + ".pac" => "application/x-ns-proxy-autoconfig", + ".swf" => "application/x-shockwave-flash", + ".tar.gz" => "application/x-tgz", + ".tgz" => "application/x-tgz", + ".tar" => "application/x-tar", + ".zip" => "application/zip", + ".mp3" => "audio/mpeg", + ".m3u" => "audio/x-mpegurl", + ".wma" => "audio/x-ms-wma", + ".wax" => "audio/x-ms-wax", + ".ogg" => "application/ogg", + ".wav" => "audio/x-wav", + ".gif" => "image/gif", + ".jpg" => "image/jpeg", + ".jpeg" => "image/jpeg", + ".png" => "image/png", + ".xbm" => "image/x-xbitmap", + ".xpm" => "image/x-xpixmap", + ".xwd" => "image/x-xwindowdump", + ".css" => "text/css", + ".html" => "text/html", + ".htm" => "text/html", + ".js" => "text/javascript", + ".asc" => "text/plain", + ".c" => "text/plain", + ".cpp" => "text/plain", + ".log" => "text/plain", + ".conf" => "text/plain", + ".text" => "text/plain", + ".txt" => "text/plain", + ".dtd" => "text/xml", + ".xml" => "text/xml", + ".mpeg" => "video/mpeg", + ".mpg" => "video/mpeg", + ".mov" => "video/quicktime", + ".qt" => "video/quicktime", + ".avi" => "video/x-msvideo", + ".asf" => "video/x-ms-asf", + ".asx" => "video/x-ms-asf", + ".wmv" => "video/x-ms-wmv", + ".bz2" => "application/x-bzip", + ".tbz" => "application/x-bzip-compressed-tar", + ".tar.bz2" => "application/x-bzip-compressed-tar", + "" => "text/plain" + ) EOF test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf" } diff --git a/git-quiltimport.sh b/git-quiltimport.sh index cebaee1cc9..9a6ba2b987 100755 --- a/git-quiltimport.sh +++ b/git-quiltimport.sh @@ -63,7 +63,7 @@ tmp_info="$tmp_dir/info" commit=$(git rev-parse HEAD) mkdir $tmp_dir || exit 2 -while read patch_name level garbage +while read patch_name level garbage <&3 do case "$patch_name" in ''|'#'*) continue;; esac case "$level" in @@ -134,5 +134,5 @@ do commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git commit-tree $tree -p $commit) && git update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4 fi -done <"$QUILT_PATCHES/series" +done 3<"$QUILT_PATCHES/series" rm -rf $tmp_dir || exit 5 diff --git a/git-rebase.sh b/git-rebase.sh index 368c0ef434..c2a9b1fbe0 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano. # -USAGE='[--interactive | -i] [-v] [--onto <newbase>] [<upstream>|--root] [<branch>]' +USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--onto <newbase>] [<upstream>|--root] [<branch>]' LONG_USAGE='git-rebase replaces <branch> with a new branch of the same name. When the --onto option is provided the new branch starts out with a HEAD equal to <newbase>, otherwise it is equal to <upstream> @@ -48,6 +48,7 @@ prec=4 verbose= git_am_opt= rebase_root= +force_rebase= continue_merge () { test -n "$prev_head" || die "prev_head must be defined" @@ -294,6 +295,11 @@ do ;; --whitespace=*) git_am_opt="$git_am_opt $1" + case "$1" in + --whitespace=fix|--whitespace=strip) + force_rebase=t + ;; + esac ;; -C*) git_am_opt="$git_am_opt $1" @@ -301,6 +307,9 @@ do --root) rebase_root=t ;; + -f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force_rebas|--force-rebase) + force_rebase=t + ;; -*) usage ;; @@ -420,10 +429,15 @@ if test "$upstream" = "$onto" && test "$mb" = "$onto" && # linear history? ! (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" - echo >&2 "Current branch $branch_name is up to date." - exit 0 + if test -z "$force_rebase" + then + # Lazily switch to the target branch if needed... + test -z "$switch_to" || git checkout "$switch_to" + echo >&2 "Current branch $branch_name is up to date." + exit 0 + else + echo "Current branch $branch_name is up to date, rebase forced." + fi fi if test -n "$verbose" diff --git a/git-send-email.perl b/git-send-email.perl index 77ca8fe880..57127aa823 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -23,7 +23,7 @@ use Getopt::Long; use Text::ParseWords; use Data::Dumper; use Term::ANSIColor; -use File::Temp qw/ tempdir /; +use File::Temp qw/ tempdir tempfile /; use Error qw(:try); use Git; @@ -68,14 +68,15 @@ git send-email [options] <file | directory | rev-list options > Automating: --identity <str> * Use the sendemail.<id> options. --cc-cmd <str> * Email Cc: via `<str> \$patch_path` - --suppress-cc <str> * author, self, sob, cccmd, all. - --[no-]signed-off-by-cc * Send to Cc: and Signed-off-by: - addresses. Default on. + --suppress-cc <str> * author, self, sob, cc, cccmd, body, bodycc, all. + --[no-]signed-off-by-cc * Send to Signed-off-by: addresses. Default on. --[no-]suppress-from * Send to self. Default off. --[no-]chain-reply-to * Chain In-Reply-To: fields. Default on. --[no-]thread * Use In-Reply-To: field. Default on. Administering: + --confirm <str> * Confirm recipients before sending; + auto, cc, compose, always, or never. --quiet * Output one line of info per email. --dry-run * Don't actually send the emails. --[no-]validate * Perform patch sanity checks. Default on. @@ -126,6 +127,7 @@ sub format_2822_time { } my $have_email_valid = eval { require Email::Valid; 1 }; +my $have_mail_address = eval { require Mail::Address; 1 }; my $smtp; my $auth; @@ -156,7 +158,7 @@ if ($@) { # Behavior modification variables my ($quiet, $dry_run) = (0, 0); my $format_patch; -my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$"; +my $compose_filename; # Handle interactive edition of files. my $multiedit; @@ -181,7 +183,7 @@ sub do_edit { my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd); my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption); my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts); -my ($validate); +my ($validate, $confirm); my (@suppress_cc); my %config_bool_settings = ( @@ -207,6 +209,7 @@ my %config_settings = ( "suppresscc" => \@suppress_cc, "envelopesender" => \$envelope_sender, "multiedit" => \$multiedit, + "confirm" => \$confirm, ); # Handle Uncouth Termination @@ -219,11 +222,13 @@ sub signal_handler { system "stty echo"; # tmp files from --compose - if (-e $compose_filename) { - print "'$compose_filename' contains an intermediate version of the email you were composing.\n"; - } - if (-e ($compose_filename . ".final")) { - print "'$compose_filename.final' contains the composed email.\n" + if (defined $compose_filename) { + if (-e $compose_filename) { + print "'$compose_filename' contains an intermediate version of the email you were composing.\n"; + } + if (-e ($compose_filename . ".final")) { + print "'$compose_filename.final' contains the composed email.\n" + } } exit; @@ -256,6 +261,7 @@ my $rc = GetOptions("sender|from=s" => \$sender, "suppress-from!" => \$suppress_from, "suppress-cc=s" => \@suppress_cc, "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc, + "confirm=s" => \$confirm, "dry-run" => \$dry_run, "envelope-sender=s" => \$envelope_sender, "thread!" => \$thread, @@ -267,6 +273,9 @@ unless ($rc) { usage(); } +die "Cannot run git format-patch from outside a repository\n" + if $format_patch and not $repo; + # Now, let's fill any that aren't set in with defaults: sub read_config { @@ -318,13 +327,13 @@ my(%suppress_cc); if (@suppress_cc) { foreach my $entry (@suppress_cc) { die "Unknown --suppress-cc field: '$entry'\n" - unless $entry =~ /^(all|cccmd|cc|author|self|sob)$/; + unless $entry =~ /^(all|cccmd|cc|author|self|sob|body|bodycc)$/; $suppress_cc{$entry} = 1; } } if ($suppress_cc{'all'}) { - foreach my $entry (qw (ccmd cc author self sob)) { + foreach my $entry (qw (ccmd cc author self sob body bodycc)) { $suppress_cc{$entry} = 1; } delete $suppress_cc{'all'}; @@ -334,6 +343,21 @@ if ($suppress_cc{'all'}) { $suppress_cc{'self'} = $suppress_from if defined $suppress_from; $suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc; +if ($suppress_cc{'body'}) { + foreach my $entry (qw (sob bodycc)) { + $suppress_cc{$entry} = 1; + } + delete $suppress_cc{'body'}; +} + +# Set confirm's default value +my $confirm_unconfigured = !defined $confirm; +if ($confirm_unconfigured) { + $confirm = scalar %suppress_cc ? 'compose' : 'auto'; +}; +die "Unknown --confirm setting: '$confirm'\n" + unless $confirm =~ /^(?:auto|cc|compose|always|never)/; + # Debugging, print out the suppressions. if (0) { print "suppressions:\n"; @@ -360,6 +384,14 @@ foreach my $entry (@bcclist) { die "Comma in --bcclist entry: $entry'\n" unless $entry !~ m/,/; } +sub parse_address_line { + if ($have_mail_address) { + return map { $_->format } Mail::Address->parse($_[0]); + } else { + return split_addrs($_[0]); + } +} + sub split_addrs { return quotewords('\s*,\s*', 1, @_); } @@ -404,6 +436,7 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) { # returns 1 if the conflict must be solved using it as a format-patch argument sub check_file_rev_conflict($) { + return unless $repo; my $f = shift; try { $repo->command('rev-parse', '--verify', '--quiet', $f); @@ -445,6 +478,8 @@ while (defined(my $f = shift @ARGV)) { } if (@rev_list_opts) { + die "Cannot run git format-patch from outside a repository\n" + unless $repo; push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts); } @@ -481,6 +516,9 @@ sub get_patch_subject($) { if ($compose) { # Note that this does not need to be secure, but we will make a small # effort to have it be unique + $compose_filename = ($repo ? + tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) : + tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1]; open(C,">",$compose_filename) or die "Failed to open for writing $compose_filename: $!"; @@ -593,7 +631,7 @@ if (!@to) { } my $to = $_; - push @to, split_addrs($to); + push @to, parse_address_line($to); $prompting++; } @@ -637,25 +675,13 @@ if (!defined $smtp_server) { $smtp_server ||= 'localhost'; # could be 127.0.0.1, too... *shrug* } -if ($compose) { - while (1) { - $_ = $term->readline("Send this email? (y|n) "); - last if defined $_; - print "\n"; - } - - if (uc substr($_,0,1) ne 'Y') { - cleanup_compose_files(); - exit(0); - } - - if ($compose > 0) { - @files = ($compose_filename . ".final", @files); - } +if ($compose && $compose > 0) { + @files = ($compose_filename . ".final", @files); } # Variables we set as part of the loop over files -our ($message_id, %mail, $subject, $reply_to, $references, $message); +our ($message_id, %mail, $subject, $reply_to, $references, $message, + $needs_confirm, $message_num); sub extract_valid_address { my $address = shift; @@ -811,6 +837,37 @@ X-Mailer: git-send-email $gitversion unshift (@sendmail_parameters, '-f', $raw_from) if(defined $envelope_sender); + if ($needs_confirm && !$dry_run) { + print "\n$header\n"; + if ($needs_confirm eq "inform") { + $confirm_unconfigured = 0; # squelch this message for the rest of this run + print " The Cc list above has been expanded by additional\n"; + print " addresses found in the patch commit message. By default\n"; + print " send-email prompts before sending whenever this occurs.\n"; + print " This behavior is controlled by the sendemail.confirm\n"; + print " configuration setting.\n"; + print "\n"; + print " For additional information, run 'git send-email --help'.\n"; + print " To retain the current behavior, but squelch this message,\n"; + print " run 'git config --global sendemail.confirm auto'.\n\n"; + } + while (1) { + chomp ($_ = $term->readline( + "Send this email? ([y]es|[n]o|[q]uit|[a]ll): " + )); + last if /^(?:yes|y|no|n|quit|q|all|a)/i; + print "\n"; + } + if (/^n/i) { + return; + } elsif (/^q/i) { + cleanup_compose_files(); + exit(0); + } elsif (/^a/i) { + $confirm = 'never'; + } + } + if ($dry_run) { # We don't want to send the email. } elsif ($smtp_server =~ m#^/#) { @@ -909,6 +966,7 @@ X-Mailer: git-send-email $gitversion $reply_to = $initial_reply_to; $references = $initial_reply_to || ''; $subject = $initial_subject; +$message_num = 0; foreach my $t (@files) { open(F,"<",$t) or die "can't open file $t"; @@ -917,91 +975,106 @@ foreach my $t (@files) { my $author_encoding; my $has_content_type; my $body_encoding; - @cc = @initial_cc; + @cc = (); @xh = (); my $input_format = undef; - my $header_done = 0; + my @header = (); $message = ""; + $message_num++; + # First unfold multiline header fields while(<F>) { - if (!$header_done) { - if (/^From /) { - $input_format = 'mbox'; - next; + last if /^\s*$/; + if (/^\s+\S/ and @header) { + chomp($header[$#header]); + s/^\s+/ /; + $header[$#header] .= $_; + } else { + push(@header, $_); + } + } + # Now parse the header + foreach(@header) { + if (/^From /) { + $input_format = 'mbox'; + next; + } + chomp; + if (!defined $input_format && /^[-A-Za-z]+:\s/) { + $input_format = 'mbox'; + } + + if (defined $input_format && $input_format eq 'mbox') { + if (/^Subject:\s+(.*)$/) { + $subject = $1; } - chomp; - if (!defined $input_format && /^[-A-Za-z]+:\s/) { - $input_format = 'mbox'; + elsif (/^From:\s+(.*)$/) { + ($author, $author_encoding) = unquote_rfc2047($1); + next if $suppress_cc{'author'}; + next if $suppress_cc{'self'} and $author eq $sender; + printf("(mbox) Adding cc: %s from line '%s'\n", + $1, $_) unless $quiet; + push @cc, $1; } - - if (defined $input_format && $input_format eq 'mbox') { - if (/^Subject:\s+(.*)$/) { - $subject = $1; - - } elsif (/^(Cc|From):\s+(.*)$/) { - if (unquote_rfc2047($2) eq $sender) { + elsif (/^Cc:\s+(.*)$/) { + foreach my $addr (parse_address_line($1)) { + if (unquote_rfc2047($addr) eq $sender) { next if ($suppress_cc{'self'}); - } - elsif ($1 eq 'From') { - ($author, $author_encoding) - = unquote_rfc2047($2); - next if ($suppress_cc{'author'}); } else { next if ($suppress_cc{'cc'}); } printf("(mbox) Adding cc: %s from line '%s'\n", - $2, $_) unless $quiet; - push @cc, $2; + $addr, $_) unless $quiet; + push @cc, $addr; } - elsif (/^Content-type:/i) { - $has_content_type = 1; - if (/charset="?([^ "]+)/) { - $body_encoding = $1; - } - push @xh, $_; - } - elsif (/^Message-Id: (.*)/i) { - $message_id = $1; - } - elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) { - push @xh, $_; - } - - } else { - # In the traditional - # "send lots of email" format, - # line 1 = cc - # line 2 = subject - # So let's support that, too. - $input_format = 'lots'; - if (@cc == 0 && !$suppress_cc{'cc'}) { - printf("(non-mbox) Adding cc: %s from line '%s'\n", - $_, $_) unless $quiet; - - push @cc, $_; - - } elsif (!defined $subject) { - $subject = $_; + } + elsif (/^Content-type:/i) { + $has_content_type = 1; + if (/charset="?([^ "]+)/) { + $body_encoding = $1; } + push @xh, $_; } - - # A whitespace line will terminate the headers - if (m/^\s*$/) { - $header_done = 1; + elsif (/^Message-Id: (.*)/i) { + $message_id = $1; + } + elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) { + push @xh, $_; } + } else { - $message .= $_; - if (/^(Signed-off-by|Cc): (.*)$/i) { - next if ($suppress_cc{'sob'}); - chomp; - my $c = $2; - chomp $c; - next if ($c eq $sender and $suppress_cc{'self'}); - push @cc, $c; - printf("(sob) Adding cc: %s from line '%s'\n", - $c, $_) unless $quiet; + # In the traditional + # "send lots of email" format, + # line 1 = cc + # line 2 = subject + # So let's support that, too. + $input_format = 'lots'; + if (@cc == 0 && !$suppress_cc{'cc'}) { + printf("(non-mbox) Adding cc: %s from line '%s'\n", + $_, $_) unless $quiet; + push @cc, $_; + } elsif (!defined $subject) { + $subject = $_; } } } + # Now parse the message body + while(<F>) { + $message .= $_; + if (/^(Signed-off-by|Cc): (.*)$/i) { + chomp; + my ($what, $c) = ($1, $2); + chomp $c; + if ($c eq $sender) { + next if ($suppress_cc{'self'}); + } else { + next if $suppress_cc{'sob'} and $what =~ /Signed-off-by/i; + next if $suppress_cc{'bodycc'} and $what =~ /Cc/i; + } + push @cc, $c; + printf("(body) Adding cc: %s from line '%s'\n", + $c, $_) unless $quiet; + } + } close F; if (defined $cc_cmd && !$suppress_cc{'cccmd'}) { @@ -1020,7 +1093,7 @@ foreach my $t (@files) { or die "(cc-cmd) failed to close pipe to '$cc_cmd'"; } - if (defined $author) { + if (defined $author and $author ne $sender) { $message = "From: $author\n\n$message"; if (defined $author_encoding) { if ($has_content_type) { @@ -1040,6 +1113,14 @@ foreach my $t (@files) { } } + $needs_confirm = ( + $confirm eq "always" or + ($confirm =~ /^(?:auto|cc)$/ && @cc) or + ($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1)); + $needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc); + + @cc = (@initial_cc, @cc); + send_message(); # set up for the next message @@ -1054,13 +1135,10 @@ foreach my $t (@files) { $message_id = undef; } -if ($compose) { - cleanup_compose_files(); -} +cleanup_compose_files(); sub cleanup_compose_files() { - unlink($compose_filename, $compose_filename . ".final"); - + unlink($compose_filename, $compose_filename . ".final") if $compose; } $smtp->quit if $smtp; diff --git a/git-svn.perl b/git-svn.perl index cbc5211d58..8be6be00c6 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -438,7 +438,17 @@ sub cmd_dcommit { die "Unable to determine upstream SVN information from ", "$head history.\nPerhaps the repository is empty."; } - $url = defined $_commit_url ? $_commit_url : $gs->full_url; + + if (defined $_commit_url) { + $url = $_commit_url; + } else { + $url = eval { command_oneline('config', '--get', + "svn-remote.$gs->{repo_id}.commiturl") }; + if (!$url) { + $url = $gs->full_url + } + } + my $last_rev = $_revision if defined $_revision; if ($url) { print "Committing to $url ...\n"; @@ -670,7 +680,11 @@ sub cmd_create_ignore { $gs->prop_walk($gs->{path}, $r, sub { my ($gs, $path, $props) = @_; # $path is of the form /path/to/dir/ - my $ignore = '.' . $path . '.gitignore'; + $path = '.' . $path; + # SVN can have attributes on empty directories, + # which git won't track + mkpath([$path]) unless -d $path; + my $ignore = $path . '.gitignore'; my $s = $props->{'svn:ignore'} or return; open(GITIGNORE, '>', $ignore) or fatal("Failed to open `$ignore' for writing: $!"); @@ -2337,7 +2351,10 @@ sub match_paths { if (my $path = $paths->{"/$self->{path}"}) { return ($path->{action} eq 'D') ? 0 : 1; } - $self->{path_regex} ||= qr/^\/\Q$self->{path}\E\//; + my $repos_root = $self->ra->{repos_root}; + my $extended_path = $self->{url} . '/' . $self->{path}; + $extended_path =~ s#^\Q$repos_root\E(/|$)##; + $self->{path_regex} ||= qr/^\/\Q$extended_path\E\//; if (grep /$self->{path_regex}/, keys %$paths) { return 1; } @@ -2417,6 +2434,7 @@ sub find_parent_branch { # do_switch works with svn/trunk >= r22312, but that # is not included with SVN 1.4.3 (the latest version # at the moment), so we can't rely on it + $self->{last_rev} = $r0; $self->{last_commit} = $parent; $ed = SVN::Git::Fetcher->new($self, $gs->{path}); $gs->ra->gs_do_switch($r0, $rev, $gs, @@ -3282,7 +3300,7 @@ sub new { sub _mark_empty_symlinks { my ($git_svn, $switch_path) = @_; my $bool = Git::config_bool('svn.brokenSymlinkWorkaround'); - return {} if (defined($bool) && ! $bool); + return {} if (!defined($bool)) || (defined($bool) && ! $bool); my %ret; my ($rev, $cmt) = $git_svn->last_rev_commit; @@ -4615,6 +4633,7 @@ package Git::SVN::Log; use strict; use warnings; use POSIX qw/strftime/; +use Time::Local; use constant commit_log_separator => ('-' x 72) . "\n"; use vars qw/$TZ $limit $color $pager $non_recursive $verbose $oneline %rusers $show_commit $incremental/; @@ -4721,7 +4740,12 @@ sub run_pager { } sub format_svn_date { - return strftime("%Y-%m-%d %H:%M:%S %z (%a, %d %b %Y)", localtime(shift)); + # some systmes don't handle or mishandle %z, so be creative. + my $t = shift || time; + my $gm = timelocal(gmtime($t)); + my $sign = qw( + + - )[ $t <=> $gm ]; + my $gmoff = sprintf("%s%02d%02d", $sign, (gmtime(abs($t - $gm)))[2,1]); + return strftime("%Y-%m-%d %H:%M:%S $gmoff (%a, %d %b %Y)", localtime($t)); } sub parse_git_date { diff --git a/gitk-git/gitk b/gitk-git/gitk index dc2a439618..1773ae63eb 100644 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -701,16 +701,17 @@ proc newvarc {view id} { } proc splitvarc {p v} { - global varcid varcstart varccommits varctok + global varcid varcstart varccommits varctok vtokmod global vupptr vdownptr vleftptr vbackptr varcix varcrow vlastins set oa $varcid($v,$p) + set otok [lindex $varctok($v) $oa] set ac $varccommits($v,$oa) set i [lsearch -exact $varccommits($v,$oa) $p] if {$i <= 0} return set na [llength $varctok($v)] # "%" sorts before "0"... - set tok "[lindex $varctok($v) $oa]%[strrep $i]" + set tok "$otok%[strrep $i]" lappend varctok($v) $tok lappend varcrow($v) {} lappend varcix($v) {} @@ -730,6 +731,9 @@ proc splitvarc {p v} { for {set b [lindex $vdownptr($v) $na]} {$b != 0} {set b [lindex $vleftptr($v) $b]} { lset vupptr($v) $b $na } + if {[string compare $otok $vtokmod($v)] <= 0} { + modify_arc $v $oa + } } proc renumbervarc {a v} { @@ -3363,7 +3367,6 @@ proc external_blame {parent_idx {line {}}} { # being given an absolute path... set f [make_relative $f] lappend cmdline $base_commit $f - puts "cmdline={$cmdline}" if {[catch {eval exec $cmdline &} err]} { error_popup "[mc "git gui blame: command failed:"] $err" } @@ -5731,7 +5734,6 @@ proc drawcommits {row {endrow {}}} { optimize_rows $ro1 0 $r2 if {$need_redisplay || $nrows_drawn > 2000} { clear_display - drawvisible } # make the lines join to already-drawn rows either side diff --git a/hash-object.c b/hash-object.c index 37e66779ab..ebb3bedb07 100644 --- a/hash-object.c +++ b/hash-object.c @@ -84,8 +84,6 @@ int main(int argc, const char **argv) git_extract_argv0_path(argv[0]); - git_config(git_default_config, NULL); - argc = parse_options(argc, argv, hash_object_options, hash_object_usage, 0); if (write_object) { @@ -95,6 +93,8 @@ int main(int argc, const char **argv) vpath = prefix_filename(prefix, prefix_length, vpath); } + git_config(git_default_config, NULL); + if (stdin_paths) { if (hashstdin) errstr = "Can't use --stdin-paths with --stdin"; @@ -573,31 +573,21 @@ static inline int hex(int v) static char *quote_ref_url(const char *base, const char *ref) { + struct strbuf buf = STRBUF_INIT; const char *cp; - char *dp, *qref; - int len, baselen, ch; + int ch; - baselen = strlen(base); - len = baselen + 2; /* '/' after base and terminating NUL */ - for (cp = ref; (ch = *cp) != 0; cp++, len++) + strbuf_addstr(&buf, base); + if (buf.len && buf.buf[buf.len - 1] != '/' && *ref != '/') + strbuf_addstr(&buf, "/"); + + for (cp = ref; (ch = *cp) != 0; cp++) if (needs_quote(ch)) - len += 2; /* extra two hex plus replacement % */ - qref = xmalloc(len); - memcpy(qref, base, baselen); - dp = qref + baselen; - *(dp++) = '/'; - for (cp = ref; (ch = *cp) != 0; cp++) { - if (needs_quote(ch)) { - *dp++ = '%'; - *dp++ = hex((ch >> 4) & 0xF); - *dp++ = hex(ch & 0xF); - } + strbuf_addf(&buf, "%%%02x", ch); else - *dp++ = ch; - } - *dp = 0; + strbuf_addch(&buf, *cp); - return qref; + return strbuf_detach(&buf, NULL); } int http_fetch_ref(const char *base, struct ref *ref) diff --git a/imap-send.c b/imap-send.c index f91293c23f..cb518eb613 100644 --- a/imap-send.c +++ b/imap-send.c @@ -135,6 +135,7 @@ struct imap_server_conf { char *pass; int use_ssl; int ssl_verify; + int use_html; }; struct imap_store_conf { @@ -1263,6 +1264,53 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data, int *uid) return DRV_OK; } +static void encode_html_chars(struct strbuf *p) +{ + int i; + for (i = 0; i < p->len; i++) { + if (p->buf[i] == '&') + strbuf_splice(p, i, 1, "&", 5); + if (p->buf[i] == '<') + strbuf_splice(p, i, 1, "<", 4); + if (p->buf[i] == '>') + strbuf_splice(p, i, 1, ">", 4); + if (p->buf[i] == '"') + strbuf_splice(p, i, 1, """, 6); + } +} +static void wrap_in_html(struct msg_data *msg) +{ + struct strbuf buf = STRBUF_INIT; + struct strbuf **lines; + struct strbuf **p; + static char *content_type = "Content-Type: text/html;\n"; + static char *pre_open = "<pre>\n"; + static char *pre_close = "</pre>\n"; + int added_header = 0; + + strbuf_attach(&buf, msg->data, msg->len, msg->len); + lines = strbuf_split(&buf, '\n'); + strbuf_release(&buf); + for (p = lines; *p; p++) { + if (! added_header) { + if ((*p)->len == 1 && *((*p)->buf) == '\n') { + strbuf_addstr(&buf, content_type); + strbuf_addbuf(&buf, *p); + strbuf_addstr(&buf, pre_open); + added_header = 1; + continue; + } + } + else + encode_html_chars(*p); + strbuf_addbuf(&buf, *p); + } + strbuf_addstr(&buf, pre_close); + strbuf_list_free(lines); + msg->len = buf.len; + msg->data = strbuf_detach(&buf, NULL); +} + #define CHUNKSIZE 0x1000 static int read_message(FILE *f, struct msg_data *msg) @@ -1339,6 +1387,7 @@ static struct imap_server_conf server = { NULL, /* pass */ 0, /* use_ssl */ 1, /* ssl_verify */ + 0, /* use_html */ }; static char *imap_folder; @@ -1377,6 +1426,8 @@ static int git_imap_config(const char *key, const char *val, void *cb) server.tunnel = xstrdup(val); else if (!strcmp("sslverify", key)) server.ssl_verify = git_config_bool(key, val); + else if (!strcmp("preformattedHTML", key)) + server.use_html = git_config_bool(key, val); return 0; } @@ -1439,6 +1490,8 @@ int main(int argc, char **argv) fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total); if (!split_msg(&all_msgs, &msg, &ofs)) break; + if (server.use_html) + wrap_in_html(&msg); r = imap_store_msg(ctx, &msg, &uid); if (r != DRV_OK) break; diff --git a/index-pack.c b/index-pack.c index f7a38079e1..7fee872533 100644 --- a/index-pack.c +++ b/index-pack.c @@ -172,9 +172,8 @@ static char *open_pack_file(char *pack_name) input_fd = 0; if (!pack_name) { static char tmpfile[PATH_MAX]; - snprintf(tmpfile, sizeof(tmpfile), - "%s/pack/tmp_pack_XXXXXX", get_object_directory()); - output_fd = xmkstemp(tmpfile); + output_fd = odb_mkstemp(tmpfile, sizeof(tmpfile), + "pack/tmp_pack_XXXXXX"); pack_name = xstrdup(tmpfile); } else output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600); @@ -794,22 +793,24 @@ static void final(const char *final_pack_name, const char *curr_pack_name, if (keep_msg) { int keep_fd, keep_msg_len = strlen(keep_msg); - if (!keep_name) { - snprintf(name, sizeof(name), "%s/pack/pack-%s.keep", - get_object_directory(), sha1_to_hex(sha1)); - keep_name = name; - } - keep_fd = open(keep_name, O_RDWR|O_CREAT|O_EXCL, 0600); + + if (!keep_name) + keep_fd = odb_pack_keep(name, sizeof(name), sha1); + else + keep_fd = open(keep_name, O_RDWR|O_CREAT|O_EXCL, 0600); + if (keep_fd < 0) { if (errno != EEXIST) - die("cannot write keep file"); + die("cannot write keep file '%s' (%s)", + keep_name, strerror(errno)); } else { if (keep_msg_len > 0) { write_or_die(keep_fd, keep_msg, keep_msg_len); write_or_die(keep_fd, "\n", 1); } if (close(keep_fd) != 0) - die("cannot write keep file"); + die("cannot close written keep file '%s' (%s)", + keep_name, strerror(errno)); report = "keep"; } } diff --git a/lockfile.c b/lockfile.c index 1db1a2fefc..3dbb2d1ff9 100644 --- a/lockfile.c +++ b/lockfile.c @@ -158,7 +158,7 @@ static int lock_file(struct lock_file *lk, const char *path, int flags) NORETURN void unable_to_lock_index_die(const char *path, int err) { - if (errno == EEXIST) { + if (err == EEXIST) { die("Unable to create '%s.lock': %s.\n\n" "If no other git process is currently running, this probably means a\n" "git process crashed in this repository earlier. Make sure no other git\n" @@ -184,7 +184,7 @@ int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags) fd = lock_file(lk, path, flags); if (fd < 0) { if (flags & LOCK_DIE_ON_ERROR) - die("unable to create '%s.lock': %s", path, strerror(errno)); + unable_to_lock_index_die(path, errno); return fd; } diff --git a/log-tree.c b/log-tree.c index 84a74e544b..63cff74350 100644 --- a/log-tree.c +++ b/log-tree.c @@ -79,18 +79,18 @@ void show_decorations(struct rev_info *opt, struct commit *commit) */ static int detect_any_signoff(char *letter, int size) { - char ch, *cp; + char *cp; int seen_colon = 0; int seen_at = 0; int seen_name = 0; int seen_head = 0; cp = letter + size; - while (letter <= --cp && (ch = *cp) == '\n') + while (letter <= --cp && *cp == '\n') continue; while (letter <= cp) { - ch = *cp--; + char ch = *cp--; if (ch == '\n') break; diff --git a/pack-write.c b/pack-write.c index b426006c58..7053538f4c 100644 --- a/pack-write.c +++ b/pack-write.c @@ -44,9 +44,7 @@ char *write_idx_file(char *index_name, struct pack_idx_entry **objects, if (!index_name) { static char tmpfile[PATH_MAX]; - snprintf(tmpfile, sizeof(tmpfile), - "%s/pack/tmp_idx_XXXXXX", get_object_directory()); - fd = xmkstemp(tmpfile); + fd = odb_mkstemp(tmpfile, sizeof(tmpfile), "pack/tmp_idx_XXXXXX"); index_name = xstrdup(tmpfile); } else { unlink(index_name); @@ -239,7 +237,7 @@ char *index_pack_lockfile(int ip_out) char packname[46]; /* - * The first thing we expects from index-pack's output + * The first thing we expect from index-pack's output * is "pack\t%40s\n" or "keep\t%40s\n" (46 bytes) where * %40s is the newly created pack SHA1 name. In the "keep" * case, we need it to remove the corresponding .keep file diff --git a/parse-options.c b/parse-options.c index 4c5d09dd25..cf71bcffd2 100644 --- a/parse-options.c +++ b/parse-options.c @@ -244,6 +244,9 @@ void parse_options_start(struct parse_opt_ctx_t *ctx, ctx->out = argv; ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0); ctx->flags = flags; + if ((flags & PARSE_OPT_KEEP_UNKNOWN) && + (flags & PARSE_OPT_STOP_AT_NON_OPTION)) + die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together"); } static int usage_with_options_internal(const char * const *, @@ -253,6 +256,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, const struct option *options, const char * const usagestr[]) { + int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); + /* we must reset ->opt, unknown short option leave it dangling */ ctx->opt = NULL; @@ -268,18 +273,18 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, if (arg[1] != '-') { ctx->opt = arg + 1; - if (*ctx->opt == 'h') + if (internal_help && *ctx->opt == 'h') return parse_options_usage(usagestr, options); switch (parse_short_opt(ctx, options)) { case -1: return parse_options_usage(usagestr, options); case -2: - return PARSE_OPT_UNKNOWN; + goto unknown; } if (ctx->opt) check_typos(arg + 1, options); while (ctx->opt) { - if (*ctx->opt == 'h') + if (internal_help && *ctx->opt == 'h') return parse_options_usage(usagestr, options); switch (parse_short_opt(ctx, options)) { case -1: @@ -292,7 +297,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, */ ctx->argv[0] = xstrdup(ctx->opt - 1); *(char *)ctx->argv[0] = '-'; - return PARSE_OPT_UNKNOWN; + goto unknown; } } continue; @@ -306,16 +311,22 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, break; } - if (!strcmp(arg + 2, "help-all")) + if (internal_help && !strcmp(arg + 2, "help-all")) return usage_with_options_internal(usagestr, options, 1); - if (!strcmp(arg + 2, "help")) + if (internal_help && !strcmp(arg + 2, "help")) return parse_options_usage(usagestr, options); switch (parse_long_opt(ctx, arg + 2, options)) { case -1: return parse_options_usage(usagestr, options); case -2: - return PARSE_OPT_UNKNOWN; + goto unknown; } + continue; +unknown: + if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN)) + return PARSE_OPT_UNKNOWN; + ctx->out[ctx->cpidx++] = ctx->argv[0]; + ctx->opt = NULL; } return PARSE_OPT_DONE; } @@ -356,6 +367,9 @@ int parse_options(int argc, const char **argv, const struct option *options, int usage_with_options_internal(const char * const *usagestr, const struct option *opts, int full) { + if (!usagestr) + return PARSE_OPT_HELP; + fprintf(stderr, "usage: %s\n", *usagestr++); while (*usagestr && **usagestr) fprintf(stderr, " or: %s\n", *usagestr++); diff --git a/parse-options.h b/parse-options.h index 912290549b..f8ef1db128 100644 --- a/parse-options.h +++ b/parse-options.h @@ -21,6 +21,8 @@ enum parse_opt_flags { PARSE_OPT_KEEP_DASHDASH = 1, PARSE_OPT_STOP_AT_NON_OPTION = 2, PARSE_OPT_KEEP_ARGV0 = 4, + PARSE_OPT_KEEP_UNKNOWN = 8, + PARSE_OPT_NO_INTERNAL_HELP = 16, }; enum parse_opt_option_flags { @@ -10,6 +10,15 @@ static char *user_format; +static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat) +{ + free(user_format); + user_format = xstrdup(cp); + if (is_tformat) + rev->use_terminator = 1; + rev->commit_format = CMIT_FMT_USERFORMAT; +} + void get_commit_format(const char *arg, struct rev_info *rev) { int i; @@ -33,12 +42,7 @@ void get_commit_format(const char *arg, struct rev_info *rev) return; } if (!prefixcmp(arg, "format:") || !prefixcmp(arg, "tformat:")) { - const char *cp = strchr(arg, ':') + 1; - free(user_format); - user_format = xstrdup(cp); - if (arg[0] == 't') - rev->use_terminator = 1; - rev->commit_format = CMIT_FMT_USERFORMAT; + save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't'); return; } for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) { @@ -50,6 +54,10 @@ void get_commit_format(const char *arg, struct rev_info *rev) return; } } + if (strchr(arg, '%')) { + save_user_format(rev, arg, 1); + return; + } die("invalid --pretty format: %s", arg); } @@ -75,8 +83,7 @@ static int get_one_line(const char *msg) /* High bit set, or ISO-2022-INT */ int non_ascii(int ch) { - ch = (ch & 0xff); - return ((ch & 0x80) || (ch == 0x1b)); + return !isascii(ch) || ch == '\033'; } static int is_rfc2047_special(char ch) @@ -568,16 +575,16 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder, return end - placeholder + 1; } if (!prefixcmp(placeholder + 1, "red")) { - strbuf_addstr(sb, "\033[31m"); + strbuf_addstr(sb, GIT_COLOR_RED); return 4; } else if (!prefixcmp(placeholder + 1, "green")) { - strbuf_addstr(sb, "\033[32m"); + strbuf_addstr(sb, GIT_COLOR_GREEN); return 6; } else if (!prefixcmp(placeholder + 1, "blue")) { - strbuf_addstr(sb, "\033[34m"); + strbuf_addstr(sb, GIT_COLOR_BLUE); return 5; } else if (!prefixcmp(placeholder + 1, "reset")) { - strbuf_addstr(sb, "\033[m"); + strbuf_addstr(sb, GIT_COLOR_RESET); return 6; } else return 0; @@ -12,15 +12,15 @@ static int rerere_autoupdate; static char *merge_rr_path; -static const char *rr_path(const char *name, const char *file) +const char *rerere_path(const char *hex, const char *file) { - return git_path("rr-cache/%s/%s", name, file); + return git_path("rr-cache/%s/%s", hex, file); } -static int has_resolution(const char *name) +int has_rerere_resolution(const char *hex) { struct stat st; - return !stat(rr_path(name, "postimage"), &st); + return !stat(rerere_path(hex, "postimage"), &st); } static void read_rr(struct string_list *rr) @@ -208,12 +208,12 @@ static int merge(const char *name, const char *path) mmbuffer_t result = {NULL, 0}; xpparam_t xpp = {XDF_NEED_MINIMAL}; - if (handle_file(path, NULL, rr_path(name, "thisimage")) < 0) + if (handle_file(path, NULL, rerere_path(name, "thisimage")) < 0) return 1; - if (read_mmfile(&cur, rr_path(name, "thisimage")) || - read_mmfile(&base, rr_path(name, "preimage")) || - read_mmfile(&other, rr_path(name, "postimage"))) + if (read_mmfile(&cur, rerere_path(name, "thisimage")) || + read_mmfile(&base, rerere_path(name, "preimage")) || + read_mmfile(&other, rerere_path(name, "postimage"))) return 1; ret = xdl_merge(&base, &cur, "", &other, "", &xpp, XDL_MERGE_ZEALOUS, &result); @@ -291,7 +291,7 @@ static int do_plain_rerere(struct string_list *rr, int fd) string_list_insert(path, rr)->util = hex; if (mkdir(git_path("rr-cache/%s", hex), 0755)) continue; - handle_file(path, NULL, rr_path(hex, "preimage")); + handle_file(path, NULL, rerere_path(hex, "preimage")); fprintf(stderr, "Recorded preimage for '%s'\n", path); } } @@ -307,7 +307,7 @@ static int do_plain_rerere(struct string_list *rr, int fd) const char *path = rr->items[i].string; const char *name = (const char *)rr->items[i].util; - if (has_resolution(name)) { + if (has_rerere_resolution(name)) { if (!merge(name, path)) { if (rerere_autoupdate) string_list_insert(path, &update); @@ -326,7 +326,7 @@ static int do_plain_rerere(struct string_list *rr, int fd) continue; fprintf(stderr, "Recorded resolution for '%s'.\n", path); - copy_file(rr_path(name, "postimage"), path, 0666); + copy_file(rerere_path(name, "postimage"), path, 0666); mark_resolved: rr->items[i].util = NULL; } @@ -5,5 +5,7 @@ extern int setup_rerere(struct string_list *); extern int rerere(void); +extern const char *rerere_path(const char *hex, const char *file); +extern int has_rerere_resolution(const char *hex); #endif diff --git a/revision.c b/revision.c index 286e416b75..c4efe5b22e 100644 --- a/revision.c +++ b/revision.c @@ -1144,9 +1144,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--pretty")) { revs->verbose_header = 1; get_commit_format(arg+8, revs); - } else if (!prefixcmp(arg, "--pretty=")) { + } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) { revs->verbose_header = 1; get_commit_format(arg+9, revs); + } else if (!strcmp(arg, "--oneline")) { + revs->verbose_header = 1; + get_commit_format("oneline", revs); + revs->abbrev_commit = 1; } else if (!strcmp(arg, "--graph")) { revs->topo_order = 1; revs->rewrite_parents = 1; diff --git a/sha1_file.c b/sha1_file.c index 5b6e0f61fa..032300c4c6 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -801,7 +801,7 @@ unsigned char* use_pack(struct packed_git *p, if (p->pack_fd == -1 && open_packed_git(p)) die("packfile %s cannot be accessed", p->pack_name); - /* Since packfiles end in a hash of their content and its + /* Since packfiles end in a hash of their content and it's * pointless to ask for an offset into the middle of that * hash, and the in_window function above wouldn't match * don't allow an offset too close to the end of the file. diff --git a/t/Makefile b/t/Makefile index ed49c20b16..09623414a7 100644 --- a/t/Makefile +++ b/t/Makefile @@ -38,4 +38,7 @@ full-svn-test: $(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C $(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=0 LC_ALL=en_US.UTF-8 -.PHONY: pre-clean $(T) aggregate-results clean +valgrind: + GIT_TEST_OPTS=--valgrind $(MAKE) + +.PHONY: pre-clean $(T) aggregate-results clean valgrind @@ -39,7 +39,8 @@ this: * passed all 3 test(s) You can pass --verbose (or -v), --debug (or -d), and --immediate -(or -i) command line argument to the test. +(or -i) command line argument to the test, or by setting GIT_TEST_OPTS +appropriately before running "make". --verbose:: This makes the test more verbose. Specifically, the @@ -58,6 +59,21 @@ You can pass --verbose (or -v), --debug (or -d), and --immediate This causes additional long-running tests to be run (where available), for more exhaustive testing. +--valgrind:: + Execute all Git binaries with valgrind and exit with status + 126 on errors (just like regular tests, this will only stop + the test script when running under -i). Valgrind errors + go to stderr, so you might want to pass the -v option, too. + + Since it makes no sense to run the tests with --valgrind and + not see any output, this option implies --verbose. For + convenience, it also implies --tee. + +--tee:: + In addition to printing the test output to the terminal, + write it to files named 't/test-results/$TEST_NAME.out'. + As the names depend on the tests' file names, it is safe to + run the tests with this option in parallel. Skipping Tests -------------- diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index 3824020ca1..86cdebc727 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -11,7 +11,21 @@ then exit fi -LIB_HTTPD_PATH=${LIB_HTTPD_PATH-'/usr/sbin/apache2'} +HTTPD_PARA="" + +case $(uname) in + Darwin) + DEFAULT_HTTPD_PATH='/usr/sbin/httpd' + DEFAULT_HTTPD_MODULE_PATH='/usr/libexec/apache2' + HTTPD_PARA="$HTTPD_PARA -DDarwin" + ;; + *) + DEFAULT_HTTPD_PATH='/usr/sbin/apache2' + DEFAULT_HTTPD_MODULE_PATH='/usr/lib/apache2/modules' + ;; +esac + +LIB_HTTPD_PATH=${LIB_HTTPD_PATH-"$DEFAULT_HTTPD_PATH"} LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'8111'} TEST_PATH="$TEST_DIRECTORY"/lib-httpd @@ -39,14 +53,12 @@ then exit fi - LIB_HTTPD_MODULE_PATH='/usr/lib/apache2/modules' + LIB_HTTPD_MODULE_PATH="$DEFAULT_HTTPD_MODULE_PATH" fi else error "Could not identify web server at '$LIB_HTTPD_PATH'" fi -HTTPD_PARA="" - prepare_httpd() { mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH" @@ -95,5 +107,5 @@ stop_httpd() { trap 'die' EXIT "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \ - -f "$TEST_PATH/apache.conf" -k stop + -f "$TEST_PATH/apache.conf" $HTTPD_PARA -k stop } diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index fdb19a50f1..f460e40416 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -4,6 +4,15 @@ DocumentRoot www LogFormat "%h %l %u %t \"%r\" %>s %b" common CustomLog access.log common ErrorLog error.log +<IfModule !mod_log_config.c> + LoadModule log_config_module modules/mod_log_config.so +</IfModule> + +<IfDefine Darwin> + LoadModule log_config_module modules/mod_log_config.so + LockFile accept.lock + PidFile httpd.pid +</IfDefine> <IfDefine SSL> LoadModule ssl_module modules/mod_ssl.so diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 11b82f43dd..3c06842d99 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -336,10 +336,10 @@ test_expect_success 'get bool variable with empty value' \ 'git config --bool emptyvalue.variable > output && cmp output expect' -git config > output 2>&1 - -test_expect_success 'no arguments, but no crash' \ - "test $? = 129 && grep usage output" +test_expect_success 'no arguments, but no crash' ' + test_must_fail git config >output 2>&1 && + grep usage output +' cat > .git/config << EOF [a.b] @@ -373,7 +373,7 @@ EOF test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect' test_expect_success 'alternative GIT_CONFIG (non-existing file should fail)' \ - 'git config --file non-existing-config -l; test $? != 0' + 'test_must_fail git config --file non-existing-config -l' cat > other-config << EOF [ein] diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 4597af0eb6..a22632f483 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -28,4 +28,71 @@ test_expect_success 'loose objects borrowed from alternate are not missing' ' ) ' +# Corruption tests follow. Make sure to remove all traces of the +# specific corruption you test afterwards, lest a later test trip over +# it. + +test_expect_success 'object with bad sha1' ' + sha=$(echo blob | git hash-object -w --stdin) && + echo $sha && + old=$(echo $sha | sed "s+^..+&/+") && + new=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff && + sha="$(dirname $new)$(basename $new)" + mv .git/objects/$old .git/objects/$new && + git update-index --add --cacheinfo 100644 $sha foo && + tree=$(git write-tree) && + cmt=$(echo bogus | git commit-tree $tree) && + git update-ref refs/heads/bogus $cmt && + (git fsck 2>out; true) && + grep "$sha.*corrupt" out && + rm -f .git/objects/$new && + git update-ref -d refs/heads/bogus && + git read-tree -u --reset HEAD +' + +test_expect_success 'branch pointing to non-commit' ' + git rev-parse HEAD^{tree} > .git/refs/heads/invalid && + git fsck 2>out && + grep "not a commit" out && + git update-ref -d refs/heads/invalid +' + +cat > invalid-tag <<EOF +object ffffffffffffffffffffffffffffffffffffffff +type commit +tag invalid +tagger T A Gger <tagger@example.com> 1234567890 -0000 + +This is an invalid tag. +EOF + +test_expect_failure 'tag pointing to nonexistent' ' + tag=$(git hash-object -w --stdin < invalid-tag) && + echo $tag > .git/refs/tags/invalid && + git fsck --tags 2>out && + cat out && + grep "could not load tagged object" out && + rm .git/refs/tags/invalid +' + +cat > wrong-tag <<EOF +object $(echo blob | git hash-object -w --stdin) +type commit +tag wrong +tagger T A Gger <tagger@example.com> 1234567890 -0000 + +This is an invalid tag. +EOF + +test_expect_failure 'tag pointing to something else than its type' ' + tag=$(git hash-object -w --stdin < wrong-tag) && + echo $tag > .git/refs/tags/wrong && + git fsck --tags 2>out && + cat out && + grep "some sane error message" out && + rm .git/refs/tags/wrong +' + + + test_done diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index 85aef12a11..c65bca8388 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh @@ -19,6 +19,9 @@ do >$dir/a.$i done done +>"#ignore1" +>"#ignore2" +>"#hidden" cat >expect <<EOF a.2 @@ -42,6 +45,9 @@ three/a.8 EOF echo '.gitignore +\#ignore1 +\#ignore2* +\#hid*n output expect .gitignore @@ -79,9 +85,10 @@ test_expect_success \ >output && test_cmp expect output' -cat > excludes-file << EOF +cat > excludes-file <<\EOF *.[1-8] e* +\#* EOF git config core.excludesFile excludes-file diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh new file mode 100755 index 0000000000..809d1c4ed4 --- /dev/null +++ b/t/t3203-branch-output.sh @@ -0,0 +1,81 @@ +#!/bin/sh + +test_description='git branch display tests' +. ./test-lib.sh + +test_expect_success 'make commits' ' + echo content >file && + git add file && + git commit -m one && + echo content >>file && + git commit -a -m two +' + +test_expect_success 'make branches' ' + git branch branch-one + git branch branch-two HEAD^ +' + +test_expect_success 'make remote branches' ' + git update-ref refs/remotes/origin/branch-one branch-one + git update-ref refs/remotes/origin/branch-two branch-two + git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/branch-one +' + +cat >expect <<'EOF' + branch-one + branch-two +* master +EOF +test_expect_success 'git branch shows local branches' ' + git branch >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' + origin/HEAD -> origin/branch-one + origin/branch-one + origin/branch-two +EOF +test_expect_success 'git branch -r shows remote branches' ' + git branch -r >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' + branch-one + branch-two +* master + remotes/origin/HEAD -> origin/branch-one + remotes/origin/branch-one + remotes/origin/branch-two +EOF +test_expect_success 'git branch -a shows local and remote branches' ' + git branch -a >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +two +one +two +EOF +test_expect_success 'git branch -v shows branch summaries' ' + git branch -v >tmp && + awk "{print \$NF}" <tmp >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +* (no branch) + branch-one + branch-two + master +EOF +test_expect_success 'git branch shows detached HEAD properly' ' + git checkout HEAD^0 && + git branch >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index 8c0c5f5982..be7ae5a004 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -48,6 +48,10 @@ test_expect_success \ 'the rebase operation should not have destroyed author information' \ '! (git log | grep "Author:" | grep "<>")' +test_expect_success 'HEAD was detached during rebase' ' + test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1}) +' + test_expect_success 'rebase after merge master' ' git reset --hard topic && git merge master && @@ -85,10 +89,6 @@ test_expect_success 'rebase a single mode change' ' GIT_TRACE=1 git rebase master ' -test_expect_success 'HEAD was detached during rebase' ' - test $(git rev-parse HEAD@{1}) != $(git rev-parse modechange@{1}) -' - test_expect_success 'Show verbose error when HEAD could not be detached' ' : > B && test_must_fail git rebase topic 2> output.err > output.out && diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh new file mode 100755 index 0000000000..9aaeabd972 --- /dev/null +++ b/t/t3505-cherry-pick-empty.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +test_description='test cherry-picking an empty commit' + +. ./test-lib.sh + +test_expect_success setup ' + + echo first > file1 && + git add file1 && + test_tick && + git commit -m "first" && + + git checkout -b empty-branch && + test_tick && + git commit --allow-empty -m "empty" + +' + +test_expect_code 1 'cherry-pick an empty commit' ' + + git checkout master && + git cherry-pick empty-branch + +' + +test_expect_success 'index lockfile was removed' ' + + test ! -f .git/index.lock + +' + +test_done diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh index cc3681f161..18695ce821 100755 --- a/t/t4002-diff-basic.sh +++ b/t/t4002-diff-basic.sh @@ -258,4 +258,12 @@ test_expect_success \ git diff-tree -r -R $tree_A $tree_B >.test-b && cmp -s .test-a .test-b' +test_expect_success \ + 'diff can read from stdin' \ + 'test_must_fail git diff --no-index -- MN - < NN | + grep -v "^index" | sed "s#/-#/NN#" >.test-a && + test_must_fail git diff --no-index -- MN NN | + grep -v "^index" >.test-b && + test_cmp .test-a .test-b' + test_done diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh index 3cf5b5c4ea..f64aa48d24 100755 --- a/t/t4012-diff-binary.sh +++ b/t/t4012-diff-binary.sh @@ -87,7 +87,7 @@ nul_to_q() { test_expect_success 'diff --no-index with binary creation' ' echo Q | q_to_nul >binary && - (:# hide error code from diff, which just indicates differences + (: hide error code from diff, which just indicates differences git diff --binary --no-index /dev/null binary >current || true ) && diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 9c709022ef..9cd5a6e685 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -207,6 +207,10 @@ log --root -c --patch-with-stat --summary master log --root --cc --patch-with-stat --summary master log -SF master log -SF -p master +log --decorate --all + +rev-list --parents HEAD +rev-list --children HEAD whatchanged master whatchanged -p master @@ -268,6 +272,7 @@ diff --no-index --name-status dir2 dir diff --no-index --name-status -- dir2 dir diff --no-index dir dir3 diff master master^ side +diff --dirstat master~1 master~2 EOF test_done diff --git a/t/t4013/diff.diff_--dirstat_master~1_master~2 b/t/t4013/diff.diff_--dirstat_master~1_master~2 new file mode 100644 index 0000000000..b672e1ca63 --- /dev/null +++ b/t/t4013/diff.diff_--dirstat_master~1_master~2 @@ -0,0 +1,3 @@ +$ git diff --dirstat master~1 master~2 + 40.0% dir/ +$ diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all new file mode 100644 index 0000000000..12da8ac07d --- /dev/null +++ b/t/t4013/diff.log_--decorate_--all @@ -0,0 +1,34 @@ +$ git log --decorate --all +commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (refs/heads/master) +Merge: 9a6d494 c7a2ab9 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:04:00 2006 +0000 + + Merge branch 'side' + +commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side) +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:03:00 2006 +0000 + + Side + +commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:02:00 2006 +0000 + + Third + +commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:01:00 2006 +0000 + + Second + + This is the second commit. + +commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial) +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:00:00 2006 +0000 + + Initial +$ diff --git a/t/t4013/diff.rev-list_--children_HEAD b/t/t4013/diff.rev-list_--children_HEAD new file mode 100644 index 0000000000..e7f17d5aa0 --- /dev/null +++ b/t/t4013/diff.rev-list_--children_HEAD @@ -0,0 +1,7 @@ +$ git rev-list --children HEAD +59d314ad6f356dd08601a4cd5e530381da3e3c64 +c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a 59d314ad6f356dd08601a4cd5e530381da3e3c64 +9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 59d314ad6f356dd08601a4cd5e530381da3e3c64 +1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 +444ac553ac7612cc88969031b02b3767fb8a353a 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a +$ diff --git a/t/t4013/diff.rev-list_--parents_HEAD b/t/t4013/diff.rev-list_--parents_HEAD new file mode 100644 index 0000000000..65d2a80208 --- /dev/null +++ b/t/t4013/diff.rev-list_--parents_HEAD @@ -0,0 +1,7 @@ +$ git rev-list --parents HEAD +59d314ad6f356dd08601a4cd5e530381da3e3c64 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a +c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a 444ac553ac7612cc88969031b02b3767fb8a353a +9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 +1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 444ac553ac7612cc88969031b02b3767fb8a353a +444ac553ac7612cc88969031b02b3767fb8a353a +$ diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 7b976ee36d..b98619035c 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -37,6 +37,46 @@ test_expect_success setup ' ' +printf "sixth\nfifth\nfourth\nthird\nsecond\ninitial" > expect +test_expect_success 'pretty' ' + + git log --pretty="format:%s" > actual && + test_cmp expect actual +' + +printf "sixth\nfifth\nfourth\nthird\nsecond\ninitial\n" > expect +test_expect_success 'pretty (tformat)' ' + + git log --pretty="tformat:%s" > actual && + test_cmp expect actual +' + +test_expect_success 'pretty (shortcut)' ' + + git log --pretty="%s" > actual && + test_cmp expect actual +' + +test_expect_success 'format' ' + + git log --format="%s" > actual && + test_cmp expect actual +' + +cat > expect << EOF +804a787 sixth +394ef78 fifth +5d31159 fourth +2fbe8c0 third +f7dab8e second +3a2fdcb initial +EOF +test_expect_success 'oneline' ' + + git log --oneline > actual && + test_cmp expect actual +' + test_expect_success 'diff-filter=A' ' actual=$(git log --pretty="format:%s" --diff-filter=A HEAD) && @@ -134,5 +174,153 @@ test_expect_success 'log --grep -i' ' test_cmp expect actual ' +cat > expect <<EOF +* Second +* sixth +* fifth +* fourth +* third +* second +* initial +EOF + +test_expect_success 'simple log --graph' ' + git log --graph --pretty=tformat:%s >actual && + test_cmp expect actual +' + +test_expect_success 'set up merge history' ' + git checkout -b side HEAD~4 && + test_commit side-1 1 1 && + test_commit side-2 2 2 && + git checkout master && + git merge side +' + +cat > expect <<\EOF +* Merge branch 'side' +|\ +| * side-2 +| * side-1 +* | Second +* | sixth +* | fifth +* | fourth +|/ +* third +* second +* initial +EOF + +test_expect_success 'log --graph with merge' ' + git log --graph --date-order --pretty=tformat:%s | + sed "s/ *$//" >actual && + test_cmp expect actual +' + +cat > expect <<\EOF +* commit master +|\ Merge: A B +| | Author: A U Thor <author@example.com> +| | +| | Merge branch 'side' +| | +| * commit side +| | Author: A U Thor <author@example.com> +| | +| | side-2 +| | +| * commit tags/side-1 +| | Author: A U Thor <author@example.com> +| | +| | side-1 +| | +* | commit master~1 +| | Author: A U Thor <author@example.com> +| | +| | Second +| | +* | commit master~2 +| | Author: A U Thor <author@example.com> +| | +| | sixth +| | +* | commit master~3 +| | Author: A U Thor <author@example.com> +| | +| | fifth +| | +* | commit master~4 +|/ Author: A U Thor <author@example.com> +| +| fourth +| +* commit tags/side-1~1 +| Author: A U Thor <author@example.com> +| +| third +| +* commit tags/side-1~2 +| Author: A U Thor <author@example.com> +| +| second +| +* commit tags/side-1~3 + Author: A U Thor <author@example.com> + + initial +EOF + +test_expect_success 'log --graph with full output' ' + git log --graph --date-order --pretty=short | + git name-rev --name-only --stdin | + sed "s/Merge:.*/Merge: A B/;s/ *$//" >actual && + test_cmp expect actual +' + +test_expect_success 'set up more tangled history' ' + git checkout -b tangle HEAD~6 && + test_commit tangle-a tangle-a a && + git merge master~3 && + git merge side~1 && + git checkout master && + git merge tangle +' + +cat > expect <<\EOF +* Merge branch 'tangle' +|\ +| * Merge branch 'side' (early part) into tangle +| |\ +| * \ Merge branch 'master' (early part) into tangle +| |\ \ +| * | | tangle-a +* | | | Merge branch 'side' +|\ \ \ \ +| * | | | side-2 +| | | |/ +| | |/| +| |/| | +| * | | side-1 +* | | | Second +* | | | sixth +| | |/ +| |/| +|/| | +* | | fifth +* | | fourth +|/ / +* | third +|/ +* second +* initial +EOF + +test_expect_success 'log --graph with merge' ' + git log --graph --date-order --pretty=tformat:%s | + sed "s/ *$//" >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4203-patch-id.sh b/t/t4203-patch-id.sh new file mode 100755 index 0000000000..04f7bae850 --- /dev/null +++ b/t/t4203-patch-id.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +test_description='git patch-id' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit initial foo a && + test_commit first foo b && + git checkout -b same HEAD^ && + test_commit same-msg foo b && + git checkout -b notsame HEAD^ && + test_commit notsame-msg foo c +' + +test_expect_success 'patch-id output is well-formed' ' + git log -p -1 | git patch-id > output && + grep "^[a-f0-9]\{40\} $(git rev-parse HEAD)$" output +' + +get_patch_id () { + git log -p -1 "$1" | git patch-id | + sed "s# .*##" > patch-id_"$1" +} + +test_expect_success 'patch-id detects equality' ' + get_patch_id master && + get_patch_id same && + test_cmp patch-id_master patch-id_same +' + +test_expect_success 'patch-id detects inequality' ' + get_patch_id master && + get_patch_id notsame && + ! test_cmp patch-id_master patch-id_notsame +' + +test_done diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index c942c8be85..b7e362834b 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -86,6 +86,10 @@ test_expect_success \ 'git archive vs. the same in a bare repo' \ 'test_cmp b.tar b3.tar' +test_expect_success 'git archive with --output' \ + 'git archive --output=b4.tar HEAD && + test_cmp b.tar b4.tar' + test_expect_success \ 'validate file modification time' \ 'mkdir extract && @@ -172,6 +176,10 @@ test_expect_success \ 'git archive --format=zip vs. the same in a bare repo' \ 'test_cmp d.zip d1.zip' +test_expect_success 'git archive --format=zip with --output' \ + 'git archive --format=zip --output=d2.zip HEAD && + test_cmp d.zip d2.zip' + $UNZIP -v >/dev/null 2>&1 if [ $? -eq 127 ]; then echo "Skipping ZIP tests, because unzip was not found" diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 04522857ab..ccfc64c6ee 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -180,6 +180,23 @@ test_expect_success \ unset GIT_OBJECT_DIRECTORY +test_expect_success 'survive missing objects/pack directory' ' + ( + rm -fr missing-pack && + mkdir missing-pack && + cd missing-pack && + git init && + GOP=.git/objects/pack + rm -fr $GOP && + git index-pack --stdin --keep=test <../test-3-${packname_3}.pack && + test -f $GOP/pack-${packname_3}.pack && + test_cmp $GOP/pack-${packname_3}.pack ../test-3-${packname_3}.pack && + test -f $GOP/pack-${packname_3}.idx && + test_cmp $GOP/pack-${packname_3}.idx ../test-3-${packname_3}.idx && + test -f $GOP/pack-${packname_3}.keep + ) +' + test_expect_success \ 'verify pack' \ 'git verify-pack test-1-${packname_1}.idx \ diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index 11b343274f..10e5fd0d5a 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -94,10 +94,15 @@ test_expect_success 'MKCOL sends directory names with trailing slashes' ' ' -test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' ' +x1="[0-9a-f]" +x2="$x1$x1" +x5="$x1$x1$x1$x1$x1" +x38="$x5$x5$x5$x5$x5$x5$x5$x1$x1$x1" +x40="$x38$x2" - grep -P "\"(?:PUT|MOVE) .+objects/[\da-z]{2}/[\da-z]{38}_[\da-z\-]{40} HTTP/[0-9.]+\" 20\d" \ - < "$HTTPD_ROOT_PATH"/access.log +test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' ' + sed -e "s/PUT /OP /" -e "s/MOVE /OP /" "$HTTPD_ROOT_PATH"/access.log | + grep -e "\"OP .*/objects/$x2/${x38}_$x40 HTTP/[.0-9]*\" 20[0-9] " ' diff --git a/t/t5705-clone-2gb.sh b/t/t5705-clone-2gb.sh new file mode 100755 index 0000000000..9f52154cac --- /dev/null +++ b/t/t5705-clone-2gb.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +test_description='Test cloning a repository larger than 2 gigabyte' +. ./test-lib.sh + +test -z "$GIT_TEST_CLONE_2GB" && +say "Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t" && +test_done && +exit + +test_expect_success 'setup' ' + + git config pack.compression 0 && + git config pack.depth 0 && + blobsize=$((20*1024*1024)) && + blobcount=$((2*1024*1024*1024/$blobsize+1)) && + i=1 && + (while test $i -le $blobcount + do + printf "Generating blob $i/$blobcount\r" >&2 && + printf "blob\nmark :$i\ndata $blobsize\n" && + #test-genrandom $i $blobsize && + printf "%-${blobsize}s" $i && + echo "M 100644 :$i $i" >> commit + i=$(($i+1)) || + echo $? > exit-status + done && + echo "commit refs/heads/master" && + echo "author A U Thor <author@email.com> 123456789 +0000" && + echo "committer C O Mitter <committer@email.com> 123456789 +0000" && + echo "data 5" && + echo ">2gb" && + cat commit) | + git fast-import && + test ! -f exit-status + +' + +test_expect_success 'clone' ' + + git clone --bare --no-hardlinks . clone + +' + +test_done diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index dd7eac84ea..052a6c90f5 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -224,6 +224,31 @@ test_expect_success 'bisect skip: cannot tell between 2 commits' ' fi ' +# $HASH1 is good, $HASH4 is both skipped and bad, we skip $HASH3 +# and $HASH2 is good, +# so we should not be able to tell the first bad commit +# among $HASH3 and $HASH4 +test_expect_success 'bisect skip: with commit both bad and skipped' ' + git bisect start && + git bisect skip && + git bisect bad && + git bisect good $HASH1 && + git bisect skip && + if git bisect good > my_bisect_log.txt + then + echo Oops, should have failed. + false + else + test $? -eq 2 && + grep "first bad commit could be any of" my_bisect_log.txt && + ! grep $HASH1 my_bisect_log.txt && + ! grep $HASH2 my_bisect_log.txt && + grep $HASH3 my_bisect_log.txt && + grep $HASH4 my_bisect_log.txt && + git bisect reset + fi +' + # We want to automatically find the commit that # introduced "Another" into hello. test_expect_success \ diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index cb3d183770..08d5b91c91 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -32,16 +32,59 @@ clean_fake_sendmail() { } test_expect_success 'Extract patches' ' - patches=`git format-patch -n HEAD^1` + patches=`git format-patch -s --cc="One <one@example.com>" --cc=two@example.com -n HEAD^1` ' +# Test no confirm early to ensure remaining tests will not hang +test_no_confirm () { + rm -f no_confirm_okay + echo n | \ + GIT_SEND_EMAIL_NOTTY=1 \ + git send-email \ + --from="Example <from@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $@ \ + $patches > stdout && + test_must_fail grep "Send this email" stdout && + > no_confirm_okay +} + +# Exit immediately to prevent hang if a no-confirm test fails +check_no_confirm () { + test -f no_confirm_okay || { + say 'No confirm test failed; skipping remaining tests to prevent hanging' + test_done + } +} + +test_expect_success 'No confirm with --suppress-cc' ' + test_no_confirm --suppress-cc=sob +' +check_no_confirm + +test_expect_success 'No confirm with --confirm=never' ' + test_no_confirm --confirm=never +' +check_no_confirm + +# leave sendemail.confirm set to never after this so that none of the +# remaining tests prompt unintentionally. +test_expect_success 'No confirm with sendemail.confirm=never' ' + git config sendemail.confirm never && + test_no_confirm --compose --subject=foo +' +check_no_confirm + test_expect_success 'Send patches' ' - git send-email --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors + git send-email --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors ' cat >expected <<\EOF !nobody@example.com! !author@example.com! +!one@example.com! +!two@example.com! EOF test_expect_success \ 'Verify commandline' \ @@ -50,13 +93,15 @@ test_expect_success \ cat >expected-show-all-headers <<\EOF 0001-Second.patch (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' +(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com' +(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@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>,<bcc@example.com> +RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<bcc@example.com> From: Example <from@example.com> To: to@example.com -Cc: cc@example.com, A <author@example.com> +Cc: cc@example.com, A <author@example.com>, One <one@example.com>, two@example.com Subject: [PATCH 1/1] Second. Date: DATE-STRING Message-Id: MESSAGE-ID-STRING @@ -70,6 +115,7 @@ EOF test_expect_success 'Show all headers' ' git send-email \ --dry-run \ + --suppress-cc=sob \ --from="Example <from@example.com>" \ --to=to@example.com \ --cc=cc@example.com \ @@ -104,6 +150,28 @@ test_expect_success 'no patch was sent' ' ! test -e commandline1 ' +test_expect_success 'Author From: in message body' ' + clean_fake_sendmail && + git send-email \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches && + sed "1,/^$/d" < msgtxt1 > msgbody1 + grep "From: A <author@example.com>" msgbody1 +' + +test_expect_success 'Author From: not in message body' ' + clean_fake_sendmail && + git send-email \ + --from="A <author@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches && + sed "1,/^$/d" < msgtxt1 > msgbody1 + ! grep "From: A <author@example.com>" msgbody1 +' + test_expect_success 'allow long lines with --no-validate' ' git send-email \ --from="Example <nobody@example.com>" \ @@ -148,15 +216,13 @@ test_set_editor "$(pwd)/fake-editor" test_expect_success '--compose works' ' clean_fake_sendmail && - echo y | \ - GIT_SEND_EMAIL_NOTTY=1 \ - git send-email \ - --compose --subject foo \ - --from="Example <nobody@example.com>" \ - --to=nobody@example.com \ - --smtp-server="$(pwd)/fake.sendmail" \ - $patches \ - 2>errors + git send-email \ + --compose --subject foo \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches \ + 2>errors ' test_expect_success 'first message is compose text' ' @@ -167,16 +233,18 @@ test_expect_success 'second message is patch' ' grep "Subject:.*Second" msgtxt2 ' -cat >expected-show-all-headers <<\EOF +cat >expected-suppress-sob <<\EOF 0001-Second.patch (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' +(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com' +(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@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> +RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<one@example.com>,<two@example.com> From: Example <from@example.com> To: to@example.com -Cc: cc@example.com, A <author@example.com> +Cc: cc@example.com, A <author@example.com>, One <one@example.com>, two@example.com Subject: [PATCH 1/1] Second. Date: DATE-STRING Message-Id: MESSAGE-ID-STRING @@ -185,10 +253,10 @@ X-Mailer: X-MAILER-STRING Result: OK EOF -test_expect_success 'sendemail.cc set' ' - git config sendemail.cc cc@example.com && +test_suppression () { git send-email \ --dry-run \ + --suppress-cc=$1 \ --from="Example <from@example.com>" \ --to=to@example.com \ --smtp-server relay.example.com \ @@ -196,20 +264,27 @@ test_expect_success 'sendemail.cc set' ' 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 + >actual-suppress-$1 && + test_cmp expected-suppress-$1 actual-suppress-$1 +} + +test_expect_success 'sendemail.cc set' ' + git config sendemail.cc cc@example.com && + test_suppression sob ' -cat >expected-show-all-headers <<\EOF +cat >expected-suppress-sob <<\EOF 0001-Second.patch (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' +(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com' +(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com' Dry-OK. Log says: Server: relay.example.com MAIL FROM:<from@example.com> -RCPT TO:<to@example.com>,<author@example.com> +RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com> From: Example <from@example.com> To: to@example.com -Cc: A <author@example.com> +Cc: A <author@example.com>, One <one@example.com>, two@example.com Subject: [PATCH 1/1] Second. Date: DATE-STRING Message-Id: MESSAGE-ID-STRING @@ -220,17 +295,166 @@ 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_suppression sob +' + +cat >expected-suppress-all <<\EOF +0001-Second.patch +Dry-OK. Log says: +Server: relay.example.com +MAIL FROM:<from@example.com> +RCPT TO:<to@example.com> +From: Example <from@example.com> +To: to@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 '--suppress-cc=all' ' + test_suppression all +' + +cat >expected-suppress-body <<\EOF +0001-Second.patch +(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' +(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com' +(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com' +Dry-OK. Log says: +Server: relay.example.com +MAIL FROM:<from@example.com> +RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com> +From: Example <from@example.com> +To: to@example.com +Cc: A <author@example.com>, One <one@example.com>, two@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 '--suppress-cc=body' ' + test_suppression body +' + +cat >expected-suppress-sob <<\EOF +0001-Second.patch +(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' +(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com' +(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com' +Dry-OK. Log says: +Server: relay.example.com +MAIL FROM:<from@example.com> +RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com> +From: Example <from@example.com> +To: to@example.com +Cc: A <author@example.com>, One <one@example.com>, two@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 '--suppress-cc=sob' ' + test_suppression sob +' + +cat >expected-suppress-bodycc <<\EOF +0001-Second.patch +(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' +(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com' +(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com' +(body) Adding cc: C O Mitter <committer@example.com> from line 'Signed-off-by: C O Mitter <committer@example.com>' +Dry-OK. Log says: +Server: relay.example.com +MAIL FROM:<from@example.com> +RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<committer@example.com> +From: Example <from@example.com> +To: to@example.com +Cc: A <author@example.com>, One <one@example.com>, two@example.com, C O Mitter <committer@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 '--suppress-cc=bodycc' ' + test_suppression bodycc +' + +cat >expected-suppress-cc <<\EOF +0001-Second.patch +(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' +(body) Adding cc: C O Mitter <committer@example.com> from line 'Signed-off-by: C O Mitter <committer@example.com>' +Dry-OK. Log says: +Server: relay.example.com +MAIL FROM:<from@example.com> +RCPT TO:<to@example.com>,<author@example.com>,<committer@example.com> +From: Example <from@example.com> +To: to@example.com +Cc: A <author@example.com>, C O Mitter <committer@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 '--suppress-cc=cc' ' + test_suppression cc +' + +test_confirm () { + echo y | \ + GIT_SEND_EMAIL_NOTTY=1 \ + git send-email \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $@ \ + $patches | grep "Send this email" +} + +test_expect_success '--confirm=always' ' + test_confirm --confirm=always --suppress-cc=all +' + +test_expect_success '--confirm=auto' ' + test_confirm --confirm=auto +' + +test_expect_success '--confirm=cc' ' + test_confirm --confirm=cc +' + +test_expect_success '--confirm=compose' ' + test_confirm --confirm=compose --compose +' + +test_expect_success 'confirm by default (due to cc)' ' + CONFIRM=$(git config --get sendemail.confirm) && + git config --unset sendemail.confirm && + test_confirm && + git config sendemail.confirm $CONFIRM +' + +test_expect_success 'confirm by default (due to --compose)' ' + CONFIRM=$(git config --get sendemail.confirm) && + git config --unset sendemail.confirm && + test_confirm --suppress-cc=all --compose + ret="$?" + git config sendemail.confirm ${CONFIRM:-never} + test $ret = "0" ' test_expect_success '--compose adds MIME for utf8 body' ' @@ -239,9 +463,7 @@ test_expect_success '--compose adds MIME for utf8 body' ' echo "echo utf8 body: àéìöú >>\"\$1\"" ) >fake-editor-utf8 && chmod +x fake-editor-utf8 && - echo y | \ GIT_EDITOR="\"$(pwd)/fake-editor-utf8\"" \ - GIT_SEND_EMAIL_NOTTY=1 \ git send-email \ --compose --subject foo \ --from="Example <nobody@example.com>" \ @@ -263,9 +485,7 @@ test_expect_success '--compose respects user mime type' ' echo " echo utf8 body: àéìöú) >\"\$1\"" ) >fake-editor-utf8-mime && chmod +x fake-editor-utf8-mime && - echo y | \ GIT_EDITOR="\"$(pwd)/fake-editor-utf8-mime\"" \ - GIT_SEND_EMAIL_NOTTY=1 \ git send-email \ --compose --subject foo \ --from="Example <nobody@example.com>" \ @@ -279,9 +499,7 @@ test_expect_success '--compose respects user mime type' ' test_expect_success '--compose adds MIME for utf8 subject' ' clean_fake_sendmail && - echo y | \ GIT_EDITOR="\"$(pwd)/fake-editor\"" \ - GIT_SEND_EMAIL_NOTTY=1 \ git send-email \ --compose --subject utf8-sübjëct \ --from="Example <nobody@example.com>" \ @@ -303,7 +521,7 @@ test_expect_success 'detects ambiguous reference/file conflict' ' test_expect_success 'feed two files' ' rm -fr outdir && git format-patch -2 -o outdir && - GIT_SEND_EMAIL_NOTTY=1 git send-email \ + git send-email \ --dry-run \ --from="Example <nobody@example.com>" \ --to=nobody@example.com \ diff --git a/t/t9131-git-svn-empty-symlink.sh b/t/t9131-git-svn-empty-symlink.sh index 20529a878c..8f35e294aa 100755 --- a/t/t9131-git-svn-empty-symlink.sh +++ b/t/t9131-git-svn-empty-symlink.sh @@ -83,6 +83,8 @@ EOF ' test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" x' +test_expect_success 'enable broken symlink workaround' \ + '(cd x && git config svn.brokenSymlinkWorkaround true)' test_expect_success '"bar" is an empty file' 'test -f x/bar && ! test -s x/bar' test_expect_success 'get "bar" => symlink fix from svn' \ '(cd x && git svn rebase)' @@ -97,4 +99,12 @@ test_expect_success 'get "bar" => symlink fix from svn' \ '(cd y && git svn rebase)' test_expect_success '"bar" does not become a symlink' '! test -L y/bar' +# svn.brokenSymlinkWorkaround is unset +test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" z' +test_expect_success '"bar" is an empty file' 'test -f z/bar && ! test -s z/bar' +test_expect_success 'get "bar" => symlink fix from svn' \ + '(cd z && git svn rebase)' +test_expect_success '"bar" does not become a symlink' '! test -L z/bar' + + test_done diff --git a/t/t9136-git-svn-recreated-branch-empty-file.sh b/t/t9136-git-svn-recreated-branch-empty-file.sh new file mode 100755 index 0000000000..733d16e0b2 --- /dev/null +++ b/t/t9136-git-svn-recreated-branch-empty-file.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +test_description='test recreated svn branch with empty files' + +. ./lib-git-svn.sh +test_expect_success 'load svn dumpfile' ' + svnadmin load "$rawsvnrepo" < "${TEST_DIRECTORY}/t9136/svn.dump" + ' + +test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x' + +test_done diff --git a/t/t9136/svn.dump b/t/t9136/svn.dump new file mode 100644 index 0000000000..6b1ce0b2e8 --- /dev/null +++ b/t/t9136/svn.dump @@ -0,0 +1,192 @@ +SVN-fs-dump-format-version: 2 + +UUID: eecae021-8f16-48da-969d-79beb8ae6ea5 + +Revision-number: 0 +Prop-content-length: 56 +Content-length: 56 + +K 8 +svn:date +V 27 +2009-02-22T00:50:56.292890Z +PROPS-END + +Revision-number: 1 +Prop-content-length: 106 +Content-length: 106 + +K 7 +svn:log +V 4 +init +K 10 +svn:author +V 8 +john.doe +K 8 +svn:date +V 27 +2009-02-22T00:50:57.192384Z +PROPS-END + +Node-path: branches +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: tags +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: trunk +Node-kind: dir +Node-action: add +Prop-content-length: 10 +Content-length: 10 + +PROPS-END + + +Node-path: trunk/file +Node-kind: file +Node-action: add +Prop-content-length: 10 +Text-content-length: 0 +Text-content-md5: d41d8cd98f00b204e9800998ecf8427e +Content-length: 10 + +PROPS-END + + +Revision-number: 2 +Prop-content-length: 105 +Content-length: 105 + +K 7 +svn:log +V 3 +1.0 +K 10 +svn:author +V 8 +john.doe +K 8 +svn:date +V 27 +2009-02-22T00:50:58.124724Z +PROPS-END + +Node-path: tags/1.0 +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 1 +Node-copyfrom-path: trunk + + +Revision-number: 3 +Prop-content-length: 111 +Content-length: 111 + +K 7 +svn:log +V 9 +1.0.1-bad +K 10 +svn:author +V 8 +john.doe +K 8 +svn:date +V 27 +2009-02-22T00:50:58.151727Z +PROPS-END + +Node-path: tags/1.0.1 +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 2 +Node-copyfrom-path: tags/1.0 + + +Revision-number: 4 +Prop-content-length: 111 +Content-length: 111 + +K 7 +svn:log +V 9 +Wrong tag +K 10 +svn:author +V 8 +john.doe +K 8 +svn:date +V 27 +2009-02-22T00:50:58.167427Z +PROPS-END + +Node-path: tags/1.0.1 +Node-action: delete + + +Revision-number: 5 +Prop-content-length: 113 +Content-length: 113 + +K 7 +svn:log +V 10 +1.0-branch +K 10 +svn:author +V 8 +john.doe +K 8 +svn:date +V 27 +2009-02-22T00:50:58.184498Z +PROPS-END + +Node-path: branches/1.0 +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 4 +Node-copyfrom-path: tags/1.0 + + +Revision-number: 6 +Prop-content-length: 113 +Content-length: 113 + +K 7 +svn:log +V 10 +1.0.1-good +K 10 +svn:author +V 8 +john.doe +K 8 +svn:date +V 27 +2009-02-22T00:50:58.200695Z +PROPS-END + +Node-path: tags/1.0.1 +Node-kind: dir +Node-action: add +Node-copyfrom-rev: 5 +Node-copyfrom-path: branches/1.0 + + diff --git a/t/test-lib.sh b/t/test-lib.sh index 0c455929e4..7a847ecbde 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -3,6 +3,22 @@ # Copyright (c) 2005 Junio C Hamano # +# if --tee was passed, write the output not only to the terminal, but +# additionally to the file test-results/$BASENAME.out, too. +case "$GIT_TEST_TEE_STARTED, $* " in +done,*) + # do not redirect again + ;; +*' --tee '*|*' --va'*) + mkdir -p test-results + BASE=test-results/$(basename "$0" .sh) + (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1; + echo $? > $BASE.exit) | tee $BASE.out + test "$(cat $BASE.exit)" = 0 + exit + ;; +esac + # Keep the original TERM for say_color ORIGINAL_TERM=$TERM @@ -94,6 +110,10 @@ do --no-python) # noop now... shift ;; + --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind) + valgrind=t; verbose=t; shift ;; + --tee) + shift ;; # was handled already *) break ;; esac @@ -434,7 +454,7 @@ test_create_repo () { repo="$1" mkdir -p "$repo" cd "$repo" || error "Cannot setup test environment" - "$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >&3 2>&4 || + "$GIT_EXEC_PATH/git" init "--template=$owd/../templates/blt/" >&3 2>&4 || error "cannot run git init -- have you built things yet?" mv .git/hooks .git/hooks-disabled cd "$owd" @@ -492,8 +512,73 @@ test_done () { # Test the binaries we have just built. The tests are kept in # t/ subdirectory and are run in 'trash directory' subdirectory. TEST_DIRECTORY=$(pwd) -PATH=$TEST_DIRECTORY/..:$PATH -GIT_EXEC_PATH=$(pwd)/.. +if test -z "$valgrind" +then + PATH=$TEST_DIRECTORY/..:$PATH + GIT_EXEC_PATH=$TEST_DIRECTORY/.. +else + make_symlink () { + test -h "$2" && + test "$1" = "$(readlink "$2")" || { + # be super paranoid + if mkdir "$2".lock + then + rm -f "$2" && + ln -s "$1" "$2" && + rm -r "$2".lock + else + while test -d "$2".lock + do + say "Waiting for lock on $2." + sleep 1 + done + fi + } + } + + make_valgrind_symlink () { + # handle only executables + test -x "$1" || return + + base=$(basename "$1") + symlink_target=$TEST_DIRECTORY/../$base + # do not override scripts + if test -x "$symlink_target" && + test ! -d "$symlink_target" && + test "#!" != "$(head -c 2 < "$symlink_target")" + then + symlink_target=../valgrind.sh + fi + case "$base" in + *.sh|*.perl) + symlink_target=../unprocessed-script + esac + # create the link, or replace it if it is out of date + make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit + } + + # override all git executables in TEST_DIRECTORY/.. + GIT_VALGRIND=$TEST_DIRECTORY/valgrind + mkdir -p "$GIT_VALGRIND"/bin + for file in $TEST_DIRECTORY/../git* $TEST_DIRECTORY/../test-* + do + make_valgrind_symlink $file + done + OLDIFS=$IFS + IFS=: + for path in $PATH + do + ls "$path"/git-* 2> /dev/null | + while read file + do + make_valgrind_symlink "$file" + done + done + IFS=$OLDIFS + PATH=$GIT_VALGRIND/bin:$PATH + GIT_EXEC_PATH=$GIT_VALGRIND/bin + export GIT_VALGRIND +fi GIT_TEMPLATE_DIR=$(pwd)/../templates/blt unset GIT_CONFIG GIT_CONFIG_NOSYSTEM=1 diff --git a/t/valgrind/.gitignore b/t/valgrind/.gitignore new file mode 100644 index 0000000000..d4ae6676d1 --- /dev/null +++ b/t/valgrind/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/templates diff --git a/t/valgrind/analyze.sh b/t/valgrind/analyze.sh new file mode 100755 index 0000000000..d8105d9fab --- /dev/null +++ b/t/valgrind/analyze.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +out_prefix=$(dirname "$0")/../test-results/valgrind.out +output= +count=0 +total_count=0 +missing_message= +new_line=' +' + +# start outputting the current valgrind error in $out_prefix.++$count, +# and the test case which failed in the corresponding .message file +start_output () { + test -z "$output" || return + + # progress + total_count=$(($total_count+1)) + test -t 2 && printf "\rFound %d errors" $total_count >&2 + + count=$(($count+1)) + output=$out_prefix.$count + : > $output + + echo "*** $1 ***" > $output.message +} + +finish_output () { + test ! -z "$output" || return + output= + + # if a test case has more than one valgrind error, we need to + # copy the last .message file to the previous errors + test -z "$missing_message" || { + while test $missing_message -lt $count + do + cp $out_prefix.$count.message \ + $out_prefix.$missing_message.message + missing_message=$(($missing_message+1)) + done + missing_message= + } +} + +# group the valgrind errors by backtrace +output_all () { + last_line= + j=0 + i=1 + while test $i -le $count + do + # output <number> <backtrace-in-one-line> + echo "$i $(tr '\n' ' ' < $out_prefix.$i)" + i=$(($i+1)) + done | + sort -t ' ' -k 2 | # order by <backtrace-in-one-line> + while read number line + do + # find duplicates, do not output backtrace twice + if test "$line" != "$last_line" + then + last_line=$line + j=$(($j+1)) + printf "\nValgrind error $j:\n\n" + cat $out_prefix.$number + printf "\nfound in:\n" + fi + # print the test case where this came from + printf "\n" + cat $out_prefix.$number.message + done +} + +handle_one () { + OLDIFS=$IFS + IFS="$new_line" + while read line + do + case "$line" in + # backtrace, possibly a new one + ==[0-9]*) + + # Does the current valgrind error have a message yet? + case "$output" in + *.message) + test -z "$missing_message" && + missing_message=$count + output= + esac + + start_output $(basename $1) + echo "$line" | + sed 's/==[0-9]*==/==valgrind==/' >> $output + ;; + # end of backtrace + '}') + test -z "$output" || { + echo "$line" >> $output + test $output = ${output%.message} && + output=$output.message + } + ;; + # end of test case + '') + finish_output + ;; + # normal line; if $output is set, print the line + *) + test -z "$output" || echo "$line" >> $output + ;; + esac + done < $1 + IFS=$OLDIFS + + # just to be safe + finish_output +} + +for test_script in "$(dirname "$0")"/../test-results/*.out +do + handle_one $test_script +done + +output_all diff --git a/t/valgrind/default.supp b/t/valgrind/default.supp new file mode 100644 index 0000000000..9e013fa3b2 --- /dev/null +++ b/t/valgrind/default.supp @@ -0,0 +1,45 @@ +{ + ignore-zlib-errors-cond + Memcheck:Cond + obj:*libz.so* +} + +{ + ignore-zlib-errors-value8 + Memcheck:Value8 + obj:*libz.so* +} + +{ + ignore-zlib-errors-value4 + Memcheck:Value4 + obj:*libz.so* +} + +{ + ignore-ldso-cond + Memcheck:Cond + obj:*ld-*.so +} + +{ + ignore-ldso-addr8 + Memcheck:Addr8 + obj:*ld-*.so +} + +{ + ignore-ldso-addr4 + Memcheck:Addr4 + obj:*ld-*.so +} + +{ + writing-data-from-zlib-triggers-even-more-errors + Memcheck:Param + write(buf) + obj:/lib/ld-*.so + fun:write_in_full + fun:write_buffer + fun:write_loose_object +} diff --git a/t/valgrind/valgrind.sh b/t/valgrind/valgrind.sh new file mode 100755 index 0000000000..582b4dca94 --- /dev/null +++ b/t/valgrind/valgrind.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +base=$(basename "$0") + +TRACK_ORIGINS= + +VALGRIND_VERSION=$(valgrind --version) +VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)') +VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)') +test 3 -gt "$VALGRIND_MAJOR" || +test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" || +TRACK_ORIGINS=--track-origins=yes + +exec valgrind -q --error-exitcode=126 \ + --leak-check=no \ + --suppressions="$GIT_VALGRIND/default.supp" \ + --gen-suppressions=all \ + $TRACK_ORIGINS \ + --log-fd=4 \ + --input-fd=4 \ + $GIT_VALGRIND_OPTIONS \ + "$GIT_VALGRIND"/../../"$base" "$@" diff --git a/templates/hooks--update.sample b/templates/hooks--update.sample index 93c605594f..a3f68ae3b4 100755 --- a/templates/hooks--update.sample +++ b/templates/hooks--update.sample @@ -43,10 +43,12 @@ allowdeletetag=$(git config --bool hooks.allowdeletetag) # check for no description projectdesc=$(sed -e '1q' "$GIT_DIR/description") -if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb." ]; then +case "$projectdesc" in +"Unnamed repository"* | "") echo "*** Project description file hasn't been set" >&2 exit 1 -fi + ;; +esac # --- Check types # if $newrev is 0000...0000, it's a commit to delete a ref. diff --git a/templates/this--description b/templates/this--description index c6f25e80b8..498b267a8c 100644 --- a/templates/this--description +++ b/templates/this--description @@ -1 +1 @@ -Unnamed repository; edit this file to name it for gitweb. +Unnamed repository; edit this file 'description' to name the repository. @@ -50,7 +50,7 @@ static int get_trace_fd(int *need_close) return fd; } - fprintf(stderr, "What does '%s' for GIT_TRACE means ?\n", trace); + fprintf(stderr, "What does '%s' for GIT_TRACE mean?\n", trace); fprintf(stderr, "If you want to trace into a file, " "then please set GIT_TRACE to an absolute pathname " "(starting with /).\n"); diff --git a/upload-pack.c b/upload-pack.c index 19c24db643..a49d872447 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -397,12 +397,11 @@ static int get_common_commits(void) static char line[1000]; unsigned char sha1[20]; char hex[41], last_hex[41]; - int len; save_commit_buffer = 0; for(;;) { - len = packet_read_line(0, line, sizeof(line)); + int len = packet_read_line(0, line, sizeof(line)); reset_timeout(); if (!len) { @@ -410,7 +409,7 @@ static int get_common_commits(void) packet_write(1, "NAK\n"); continue; } - len = strip(line, len); + strip(line, len); if (!prefixcmp(line, "have ")) { switch (got_sha1(line+5, sha1)) { case -1: /* they have what we do not */ @@ -645,7 +644,7 @@ int main(int argc, char **argv) dir = argv[i]; if (!enter_repo(dir, strict)) - die("'%s': unable to chdir or not a git archive", dir); + die("'%s' does not appear to be a git repository", dir); if (is_repository_shallow()) die("attempt to fetch/clone from a shallow repository"); if (getenv("GIT_DEBUG_SEND_PACK")) @@ -256,3 +256,36 @@ int git_inflate(z_streamp strm, int flush) error("inflate: %s (%s)", err, strm->msg ? strm->msg : "no message"); return ret; } + +int odb_mkstemp(char *template, size_t limit, const char *pattern) +{ + int fd; + + snprintf(template, limit, "%s/%s", + get_object_directory(), pattern); + fd = mkstemp(template); + if (0 <= fd) + return fd; + + /* slow path */ + /* some mkstemp implementations erase template on failure */ + snprintf(template, limit, "%s/%s", + get_object_directory(), pattern); + safe_create_leading_directories(template); + return xmkstemp(template); +} + +int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1) +{ + int fd; + + snprintf(name, namesz, "%s/pack/pack-%s.keep", + get_object_directory(), sha1_to_hex(sha1)); + fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600); + if (0 <= fd) + return fd; + + /* slow path */ + safe_create_leading_directories(name); + return open(name, O_RDWR|O_CREAT|O_EXCL, 0600); +} diff --git a/wt-status.c b/wt-status.c index 96ff2f8f56..dd87339ff7 100644 --- a/wt-status.c +++ b/wt-status.c @@ -15,11 +15,11 @@ int wt_status_relative_paths = 1; int wt_status_use_color = -1; int wt_status_submodule_summary; static char wt_status_colors[][COLOR_MAXLEN] = { - "", /* WT_STATUS_HEADER: normal */ - "\033[32m", /* WT_STATUS_UPDATED: green */ - "\033[31m", /* WT_STATUS_CHANGED: red */ - "\033[31m", /* WT_STATUS_UNTRACKED: red */ - "\033[31m", /* WT_STATUS_NOBRANCH: red */ + GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */ + GIT_COLOR_GREEN, /* WT_STATUS_UPDATED */ + GIT_COLOR_RED, /* WT_STATUS_CHANGED */ + GIT_COLOR_RED, /* WT_STATUS_UNTRACKED */ + GIT_COLOR_RED, /* WT_STATUS_NOBRANCH */ }; enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES; diff --git a/xdiff-interface.c b/xdiff-interface.c index d782f06d99..b9b0db8d86 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -15,11 +15,10 @@ static int parse_num(char **cp_p, int *num_p) { char *cp = *cp_p; int num = 0; - int read_some; while ('0' <= *cp && *cp <= '9') num = num * 10 + *cp++ - '0'; - if (!(read_some = cp - *cp_p)) + if (!(cp - *cp_p)) return -1; *cp_p = cp; *num_p = num; |