diff options
155 files changed, 2754 insertions, 693 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 6ed31595ba..ad060f4f89 100644 --- a/Documentation/RelNotes-1.6.2.txt +++ b/Documentation/RelNotes-1.6.2.txt @@ -10,9 +10,17 @@ 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 transition plan. +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.1 @@ -40,8 +48,17 @@ Updates since v1.6.1 * @{-1} is a way to refer to the last branch you were on. This is accepted not only where an object name is expected, but anywhere - a branch name is expected. E.g. "git branch --track mybranch @{-1}" - "git rev-parse --symbolic-full-name @{-1}". + a branch name is expected and acts as if you typed the branch name. + E.g. "git branch --track mybranch @{-1}", "git merge @{-1}", and + "git rev-parse --symbolic-full-name @{-1}" would work as expected. + +* When refs/remotes/origin/HEAD points at a remote tracking branch that + has been pruned away, many git operations issued warning when they + internally enumerated the refs. We now warn only when you say "origin" + to refer to that pruned branch. + +* The location of .mailmap file can be configured, and its file format was + enhanced to allow mapping an incorrect e-mail field as well. * "git add -p" learned 'g'oto action to jump directly to a hunk. @@ -91,6 +108,9 @@ Updates since v1.6.1 * "git fsck" now checks loose objects in alternate object stores, instead of misreporting them as missing. +* "git gc --prune" was resurrected to allow "git gc --no-prune" and + giving non-default expiration period e.g. "git gc --prune=now". + * "git grep -w" and "git grep" for fixed strings have been optimized. * "git mergetool" learned -y(--no-prompt) option to disable prompting. @@ -102,6 +122,8 @@ Updates since v1.6.1 "git checkout" switches branches, taking the local changes while switching to another commit. +* "git submodule update" learned --no-fetch option. + * "git tag" learned --contains that works the same way as the same option from "git branch". @@ -128,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. @@ -137,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 -O=v1.6.1.3-371-gc19923a -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..2ab2328316 --- /dev/null +++ b/Documentation/RelNotes-1.6.3.txt @@ -0,0 +1,44 @@ +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) + + +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. + diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index ba07c8c571..8d818a2160 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -376,9 +376,36 @@ Thunderbird (A Large Angry SCM) +By default, Thunderbird will both wrap emails as well as flag them as +being 'format=flowed', both of which will make the resulting email unusable +by git. + Here are some hints on how to successfully submit patches inline using Thunderbird. +There are two different approaches. One approach is to configure +Thunderbird to not mangle patches. The second approach is to use +an external editor to keep Thunderbird from mangling the patches. + +Approach #1 (configuration): + +This recipe is current as of Thunderbird 2.0.0.19. Three steps: + 1. Configure your mail server composition as plain text + Edit...Account Settings...Composition & Addressing, + uncheck 'Compose Messages in HTML'. + 2. Configure your general composition window to not wrap + Edit..Preferences..Composition, wrap plain text messages at 0 + 3. Disable the use of format=flowed + Edit..Preferences..Advanced..Config Editor. Search for: + mailnews.send_plaintext_flowed + toggle it to make sure it is set to 'false'. + +After that is done, you should be able to compose email as you +otherwise would (cut + paste, git-format-patch | git-imap-send, etc), +and the patches should not be mangled. + +Approach #2 (external editor): + This recipe appears to work with the current [*1*] Thunderbird from Suse. The following Thunderbird extensions are needed: @@ -464,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: @@ -476,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. @@ -486,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 1ab1b96cf9..df2a7c1641 100644 --- a/Documentation/blame-options.txt +++ b/Documentation/blame-options.txt @@ -41,6 +41,13 @@ of lines before or after the line given by <start>. -S <revs-file>:: Use revs from revs-file instead of calling linkgit:git-rev-list[1]. +--reverse:: + Walk history forward instead of backward. Instead of showing + the revision in which a line appeared, this shows the last + revision in which a line has existed. This requires a range of + revision like START..END where the path to blame exists in + START. + -p:: --porcelain:: Show in a format designed for machine consumption. @@ -67,7 +74,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 @@ -83,8 +90,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/config.txt b/Documentation/config.txt index 1806a60610..f5152c5038 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1019,6 +1019,14 @@ log.showroot:: Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which normally hide the root commit will now show it. True by default. +mailmap.file:: + The location of an augmenting mailmap file. The default + mailmap, located in the root of the repository, is loaded + first, then the mailmap file pointed to by this variable. + The location of the mailmap file may be in a repository + subdirectory, or somewhere outside of the repository itself. + See linkgit:git-shortlog[1] and linkgit:git-blame[1]. + man.viewer:: Specify the programs that may be used to display help in the 'man' format. See linkgit:git-help[1]. diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 7c129cb24f..e4c711bbd2 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: 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-blame.txt b/Documentation/git-blame.txt index fba374d652..4ef54d6602 100644 --- a/Documentation/git-blame.txt +++ b/Documentation/git-blame.txt @@ -10,7 +10,7 @@ SYNOPSIS [verse] 'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [--incremental] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--since=<date>] - [<rev> | --contents <file>] [--] <file> + [<rev> | --contents <file> | --reverse <rev>] [--] <file> DESCRIPTION ----------- @@ -184,6 +184,12 @@ there is ever added information (like the commit encoding or extended commit commentary), a blame viewer won't ever care. +MAPPING AUTHORS +--------------- + +include::mailmap.txt[] + + SEE ALSO -------- linkgit:git-annotate[1] diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index 451950bab6..7ffe03f427 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -212,6 +212,11 @@ git filter-branch --index-filter 'git rm --cached filename' HEAD Now, you will get the rewritten history saved in HEAD. +As with using `rm filename`, `git rm --cached filename` will fail +if the file is absent from the tree of a commit. If it is not important +whether the file is already absent from the tree, you can use +`git rm --cached --ignore-unmatch filename` instead. + To rewrite the repository to look as if `foodir/` had been its project root, and discard all other history: @@ -334,6 +339,47 @@ git filter-branch --index-filter \ --------------------------------------------------------------- + +Checklist for Shrinking a Repository +------------------------------------ + +git-filter-branch is often used to get rid of a subset of files, +usually with some combination of `\--index-filter` and +`\--subdirectory-filter`. People expect the resulting repository to +be smaller than the original, but you need a few more steps to +actually make it smaller, because git tries hard not to lose your +objects until you tell it to. First make sure that: + +* You really removed all variants of a filename, if a blob was moved + over its lifetime. `git log \--name-only \--follow \--all \-- + filename` can help you find renames. + +* You really filtered all refs: use `\--tag-name-filter cat \-- + \--all` when calling git-filter-branch. + +Then there are two ways to get a smaller repository. A safer way is +to clone, that keeps your original intact. + +* Clone it with `git clone +++file:///path/to/repo+++`. The clone + will not have the removed objects. See linkgit:git-clone[1]. (Note + that cloning with a plain path just hardlinks everything!) + +If you really don't want to clone it, for whatever reasons, check the +following points instead (in this order). This is a very destructive +approach, so *make a backup* or go back to cloning it. You have been +warned. + +* Remove the original refs backed up by git-filter-branch: say `git + for-each-ref \--format="%(refname)" refs/original/ | xargs -n 1 git + update-ref -d`. + +* Expire all reflogs with `git reflog expire \--expire=now \--all`. + +* Garbage collect all unreferenced objects with `git gc \--prune=now` + (or if your git-gc is not new enough to support arguments to + `\--prune`, use `git repack -ad; git prune` instead). + + Author ------ Written by Petr "Pasky" Baudis <pasky@suse.cz>, diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt index 7086eea74a..b292e9843a 100644 --- a/Documentation/git-gc.txt +++ b/Documentation/git-gc.txt @@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository SYNOPSIS -------- -'git gc' [--aggressive] [--auto] [--quiet] +'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune] DESCRIPTION ----------- @@ -59,6 +59,14 @@ are consolidated into a single pack by using the `-A` option of 'git-repack'. Setting `gc.autopacklimit` to 0 disables automatic consolidation of packs. +--prune=<date>:: + Prune loose objects older than date (default is 2 weeks ago, + overrideable by the config variable `gc.pruneExpire`). This + option is on by default. + +--no-prune:: + Do not prune any loose objects. + --quiet:: Suppress all progress reports. diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt index bd49a0aee8..1685f04efe 100644 --- a/Documentation/git-imap-send.txt +++ b/Documentation/git-imap-send.txt @@ -98,6 +98,20 @@ Using direct mode with SSL: .......................... +CAUTION +------- +It is still your responsibility to make sure that the email message +sent by your email program meets the standards of your project. +Many projects do not like patches to be attached. Some mail +agents will transform patches (e.g. wrap lines, send them as +format=flowed) in ways that make them fail. You will get angry +flames ridiculing you if you don't check this. + +Thunderbird in particular is known to be problematic. Thunderbird +users may wish to visit this web page for more information: + http://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email + + BUGS ---- Doesn't handle lines starting with "From " in the message body. diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 7d1eced7d2..4e7e5a719a 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -9,7 +9,7 @@ git-push - Update remote refs along with associated objects SYNOPSIS -------- [verse] -'git push' [--all | --mirror] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] +'git push' [--all | --mirror | --tags] [--dry-run] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v | --verbose] [<repository> <refspec>...] @@ -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 3d6d429e5e..da3c38cd60 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -243,9 +243,10 @@ 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. -i:: --interactive:: diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index ff4aeff4e6..66bf3b2fcd 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -19,6 +19,19 @@ The header of the email is configurable by command line options. If not specified on the command line, the user will be prompted with a ReadLine enabled interface to provide the necessary information. +There are two formats accepted for patch files: + +1. mbox format files ++ +This is what linkgit:git-format-patch[1] generates. Most headers and MIME +formatting are ignored. + +2. The original format used by Greg Kroah-Hartman's 'send_lots_of_email.pl' +script ++ +This format expects the first line of the file to contain the "Cc:" value +and the "Subject:" of the message as the second line. + OPTIONS ------- diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt index 498bd28929..42463a955d 100644 --- a/Documentation/git-shortlog.txt +++ b/Documentation/git-shortlog.txt @@ -45,45 +45,16 @@ OPTIONS and subsequent lines are indented by `indent2` spaces. `width`, `indent1`, and `indent2` default to 76, 6 and 9 respectively. -FILES ------ - -If a file `.mailmap` exists at the toplevel of the repository, -it is used to map an author email address to a canonical real name. This -can be used to coalesce together commits by the same person where their -name was spelled differently (whether with the same email address or -not). - -Each line in the file consists, in this order, of the canonical real name -of an author, whitespace, and an email address (enclosed by '<' and '>') -to map to the name. Use hash '#' for comments, either on their own line, -or after the email address. - -A canonical name may appear in more than one line, associated with -different email addresses, but it doesn't make sense for a given address -to appear more than once (if that happens, a later line overrides the -earlier ones). - -So, for example, if your history contains commits by two authors, Jane -and Joe, whose names appear in the repository under several forms: - ------------- -Joe Developer <joe@example.com> -Joe R. Developer <joe@example.com> -Jane Doe <jane@example.com> -Jane Doe <jane@laptop.(none)> -Jane D. <jane@desktop.(none)> ------------- - -Then, supposing Joe wants his middle name initial used, and Jane prefers -her family name fully spelled out, a proper `.mailmap` file would look like: - ------------- -# Note how we don't need an entry for <jane@laptop.(none)>, because the -# real name of that author is correct already, and coalesced directly. -Jane Doe <jane@desktop.(none)> -Joe R. Developer <joe@example.com> ------------- + +MAPPING AUTHORS +--------------- + +The `.mailmap` feature is used to coalesce together commits by the same +person in the shortlog, where their name and/or email address was +spelled differently. + +include::mailmap.txt[] + Author ------ diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 2f207fbbda..3b8df44673 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -12,7 +12,7 @@ SYNOPSIS 'git submodule' [--quiet] add [-b branch] [--] <repository> <path> 'git submodule' [--quiet] status [--cached] [--] [<path>...] 'git submodule' [--quiet] init [--] [<path>...] -'git submodule' [--quiet] update [--init] [--] [<path>...] +'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--] [<path>...] 'git submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...] 'git submodule' [--quiet] foreach <command> 'git submodule' [--quiet] sync [--] [<path>...] @@ -172,6 +172,11 @@ OPTIONS (the default). This limit only applies to modified submodules. The size is always limited to 1 for added/deleted/typechanged submodules. +-N:: +--no-fetch:: + This option is only valid for the update command. + Don't fetch new objects from the remote site. + <path>...:: Paths to submodule(s). When specified this will restrict the command to only operate on the submodules found at the specified paths. diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 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/mailmap.txt b/Documentation/mailmap.txt new file mode 100644 index 0000000000..e25b154838 --- /dev/null +++ b/Documentation/mailmap.txt @@ -0,0 +1,75 @@ +If the file `.mailmap` exists at the toplevel of the repository, or at +the location pointed to by the mailmap.file configuration option, it +is used to map author and committer names and email addresses to +canonical real names and email addresses. + +In the simple form, each line in the file consists of the canonical +real name of an author, whitespace, and an email address used in the +commit (enclosed by '<' and '>') to map to the name. Thus, looks like +this +-- + Proper Name <commit@email.xx> +-- + +The more complex forms are +-- + <proper@email.xx> <commit@email.xx> +-- +which allows mailmap to replace only the email part of a commit, and +-- + Proper Name <proper@email.xx> <commit@email.xx> +-- +which allows mailmap to replace both the name and the email of a +commit matching the specified commit email address, and +-- + Proper Name <proper@email.xx> Commit Name <commit@email.xx> +-- +which allows mailmap to replace both the name and the email of a +commit matching both the specified commit name and email address. + +Example 1: Your history contains commits by two authors, Jane +and Joe, whose names appear in the repository under several forms: + +------------ +Joe Developer <joe@example.com> +Joe R. Developer <joe@example.com> +Jane Doe <jane@example.com> +Jane Doe <jane@laptop.(none)> +Jane D. <jane@desktop.(none)> +------------ + +Now suppose that Joe wants his middle name initial used, and Jane +prefers her family name fully spelled out. A proper `.mailmap` file +would look like: + +------------ +Jane Doe <jane@desktop.(none)> +Joe R. Developer <joe@example.com> +------------ + +Note how we don't need an entry for <jane@laptop.(none)>, because the +real name of that author is correct already. + +Example 2: Your repository contains commits from the following +authors: + +------------ +nick1 <bugs@company.xx> +nick2 <bugs@company.xx> +nick2 <nick2@company.xx> +santa <me@company.xx> +claus <me@company.xx> +CTO <cto@coompany.xx> +------------ + +Then, you might want a `.mailmap` file looking like: +------------ +<cto@company.xx> <cto@coompany.xx> +Some Dude <some@dude.xx> nick1 <bugs@company.xx> +Other Author <other@author.xx> nick2 <bugs@company.xx> +Other Author <other@author.xx> <nick2@company.xx> +Santa Claus <santa.claus@northpole.xx> <me@company.xx> +------------ + +Use hash '#' for comments that are either on their own line, or after +the email address.
\ No newline at end of file diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 3d87d3edd5..159390c35a 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -101,16 +101,18 @@ The placeholders are: - '%P': parent hashes - '%p': abbreviated parent hashes - '%an': author name -- '%aN': author name (respecting .mailmap) +- '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1]) - '%ae': author email +- '%aE': author email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1]) - '%ad': author date (format respects --date= option) - '%aD': author date, RFC2822 style - '%ar': author date, relative - '%at': author date, UNIX timestamp - '%ai': author date, ISO 8601 format - '%cn': committer name -- '%cN': committer name (respecting .mailmap) +- '%cN': committer name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1]) - '%ce': committer email +- '%cE': committer email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1]) - '%cd': committer date - '%cD': committer date, RFC2822 style - '%cr': committer date, relative diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index b9f6e4d1b7..5076322877 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -13,7 +13,7 @@ include::pretty-options.txt[] Synonym for `--date=relative`. ---date={relative,local,default,iso,rfc,short}:: +--date={relative,local,default,iso,rfc,short,raw}:: Only takes effect for dates shown in human-readable format, such as when using "--pretty". `log.date` config variable sets a default @@ -31,6 +31,8 @@ format, often found in E-mail messages. + `--date=short` shows only date but not time, in `YYYY-MM-DD` format. + +`--date=raw` shows the date in the internal raw git format `%s %z` format. ++ `--date=default` shows timestamps in the original timezone (either committer's or author's). 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 || \ @@ -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 @@ -239,6 +239,19 @@ static void parse_treeish_arg(const char **argv, ar_args->time = archive_time; } +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); + } +} + #define OPT__COMPR(s, v, h, p) \ { OPTION_SET_INT, (s), NULL, (v), NULL, (h), \ PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, (p) } @@ -253,6 +266,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 +276,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), @@ -294,6 +310,9 @@ static int parse_archive_args(int argc, const char **argv, if (!base) base = ""; + if (output) + create_output_file(output); + if (list) { for (i = 0; i < ARRAY_SIZE(archivers); i++) printf("%s\n", archivers[i].name); @@ -103,14 +103,22 @@ void create_branch(const char *head, struct ref_lock *lock; struct commit *commit; unsigned char sha1[20]; - char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20]; + char *real_ref, msg[PATH_MAX + 20]; + struct strbuf ref = STRBUF_INIT; int forcing = 0; + int len; - snprintf(ref, sizeof ref, "refs/heads/%s", name); - if (check_ref_format(ref)) + len = strlen(name); + if (interpret_nth_last_branch(name, &ref) != len) { + strbuf_reset(&ref); + strbuf_add(&ref, name, len); + } + strbuf_splice(&ref, 0, 0, "refs/heads/", 11); + + if (check_ref_format(ref.buf)) die("'%s' is not a valid branch name.", name); - if (resolve_ref(ref, sha1, 1, NULL)) { + if (resolve_ref(ref.buf, sha1, 1, NULL)) { if (!force) die("A branch named '%s' already exists.", name); else if (!is_bare_repository() && !strcmp(head, name)) @@ -142,7 +150,7 @@ void create_branch(const char *head, die("Not a valid branch point: '%s'.", start_name); hashcpy(sha1, commit->object.sha1); - lock = lock_any_ref_for_update(ref, NULL, 0); + lock = lock_any_ref_for_update(ref.buf, NULL, 0); if (!lock) die("Failed to lock ref for update: %s.", strerror(errno)); @@ -162,6 +170,7 @@ void create_branch(const char *head, if (write_ref_sha1(lock, sha1, msg) < 0) die("Failed to write ref: %s.", strerror(errno)); + strbuf_release(&ref); free(real_ref); } 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-blame.c b/builtin-blame.c index 9b9f5442a2..114a214ed3 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -1264,11 +1264,12 @@ struct commit_info * Parse author/committer line in the commit object buffer */ static void get_ac_line(const char *inbuf, const char *what, - int bufsz, char *person, const char **mail, + int person_len, char *person, + int mail_len, char *mail, unsigned long *time, const char **tz) { int len, tzlen, maillen; - char *tmp, *endp, *timepos; + char *tmp, *endp, *timepos, *mailpos; tmp = strstr(inbuf, what); if (!tmp) @@ -1279,10 +1280,11 @@ static void get_ac_line(const char *inbuf, const char *what, len = strlen(tmp); else len = endp - tmp; - if (bufsz <= len) { + if (person_len <= len) { error_out: /* Ugh */ - *mail = *tz = "(unknown)"; + *tz = "(unknown)"; + strcpy(mail, *tz); *time = 0; return; } @@ -1305,9 +1307,10 @@ static void get_ac_line(const char *inbuf, const char *what, *tmp = 0; while (*tmp != ' ') tmp--; - *mail = tmp + 1; + mailpos = tmp + 1; *tmp = 0; maillen = timepos - tmp; + memcpy(mail, mailpos, maillen); if (!mailmap.nr) return; @@ -1316,20 +1319,23 @@ static void get_ac_line(const char *inbuf, const char *what, * mailmap expansion may make the name longer. * make room by pushing stuff down. */ - tmp = person + bufsz - (tzlen + 1); + tmp = person + person_len - (tzlen + 1); memmove(tmp, *tz, tzlen); tmp[tzlen] = 0; *tz = tmp; - tmp = tmp - (maillen + 1); - memmove(tmp, *mail, maillen); - tmp[maillen] = 0; - *mail = tmp; - /* - * Now, convert e-mail using mailmap + * Now, convert both name and e-mail using mailmap */ - map_email(&mailmap, tmp + 1, person, tmp-person-1); + if(map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) { + /* Add a trailing '>' to email, since map_user returns plain emails + Note: It already has '<', since we replace from mail+1 */ + mailpos = memchr(mail, '\0', mail_len); + if (mailpos && mailpos-mail < mail_len - 1) { + *mailpos = '>'; + *(mailpos+1) = '\0'; + } + } } static void get_commit_info(struct commit *commit, @@ -1338,8 +1344,10 @@ static void get_commit_info(struct commit *commit, { int len; char *tmp, *endp, *reencoded, *message; - static char author_buf[1024]; - static char committer_buf[1024]; + static char author_name[1024]; + static char author_mail[1024]; + static char committer_name[1024]; + static char committer_mail[1024]; static char summary_buf[1024]; /* @@ -1357,9 +1365,11 @@ static void get_commit_info(struct commit *commit, } reencoded = reencode_commit_message(commit, NULL); message = reencoded ? reencoded : commit->buffer; - ret->author = author_buf; + ret->author = author_name; + ret->author_mail = author_mail; get_ac_line(message, "\nauthor ", - sizeof(author_buf), author_buf, &ret->author_mail, + sizeof(author_name), author_name, + sizeof(author_mail), author_mail, &ret->author_time, &ret->author_tz); if (!detailed) { @@ -1367,9 +1377,11 @@ static void get_commit_info(struct commit *commit, return; } - ret->committer = committer_buf; + ret->committer = committer_name; + ret->committer_mail = committer_mail; get_ac_line(message, "\ncommitter ", - sizeof(committer_buf), committer_buf, &ret->committer_mail, + sizeof(committer_name), committer_name, + sizeof(committer_mail), committer_mail, &ret->committer_time, &ret->committer_tz); ret->summary = summary_buf; @@ -2396,7 +2408,7 @@ parse_done: die("reading graft file %s failed: %s", revs_file, strerror(errno)); - read_mailmap(&mailmap, ".mailmap", NULL); + read_mailmap(&mailmap, NULL); if (!incremental) setup_pager(); diff --git a/builtin-branch.c b/builtin-branch.c index 56a1971d69..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); } @@ -99,6 +99,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) const char *fmt, *remote; int i; int ret = 0; + struct strbuf bname = STRBUF_INIT; switch (kinds) { case REF_REMOTE_BRANCH: @@ -119,20 +120,25 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) if (!head_rev) die("Couldn't look up commit object for HEAD"); } - for (i = 0; i < argc; i++) { - if (kinds == REF_LOCAL_BRANCH && !strcmp(head, argv[i])) { + for (i = 0; i < argc; i++, strbuf_release(&bname)) { + int len = strlen(argv[i]); + + if (interpret_nth_last_branch(argv[i], &bname) != len) + strbuf_add(&bname, argv[i], len); + + if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) { error("Cannot delete the branch '%s' " - "which you are currently on.", argv[i]); + "which you are currently on.", bname.buf); ret = 1; continue; } free(name); - name = xstrdup(mkpath(fmt, argv[i])); + name = xstrdup(mkpath(fmt, bname.buf)); if (!resolve_ref(name, sha1, 1, NULL)) { error("%sbranch '%s' not found.", - remote, argv[i]); + remote, bname.buf); ret = 1; continue; } @@ -152,22 +158,23 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) if (!force && !in_merge_bases(rev, &head_rev, 1)) { error("The branch '%s' is not an ancestor of " - "your current HEAD.\n" - "If you are sure you want to delete it, " - "run 'git branch -D %s'.", argv[i], argv[i]); + "your current HEAD.\n" + "If you are sure you want to delete it, " + "run 'git branch -D %s'.", bname.buf, bname.buf); ret = 1; continue; } if (delete_ref(name, sha1, 0)) { error("Error deleting %sbranch '%s'", remote, - argv[i]); + bname.buf); ret = 1; } else { struct strbuf buf = STRBUF_INIT; - printf("Deleted %sbranch %s (%s).\n", remote, argv[i], - find_unique_abbrev(sha1, DEFAULT_ABBREV)); - strbuf_addf(&buf, "branch.%s", argv[i]); + printf("Deleted %sbranch %s (%s).\n", remote, + bname.buf, + find_unique_abbrev(sha1, DEFAULT_ABBREV)); + strbuf_addf(&buf, "branch.%s", bname.buf); if (git_config_rename_section(buf.buf, NULL) < 0) warning("Update of config-file failed"); strbuf_release(&buf); @@ -181,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; }; @@ -193,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); @@ -239,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; } @@ -250,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); } @@ -292,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 ****"; @@ -333,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; } @@ -390,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); } @@ -402,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 f73029e2ba..c338910b1c 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -350,6 +350,19 @@ static struct ref *write_remote_refs(const struct ref *refs, return local_refs; } +static void install_branch_config(const char *local, + const char *origin, + const char *remote) +{ + struct strbuf key = STRBUF_INIT; + strbuf_addf(&key, "branch.%s.remote", local); + git_config_set(key.buf, origin); + strbuf_reset(&key); + strbuf_addf(&key, "branch.%s.merge", local); + git_config_set(key.buf, remote); + strbuf_release(&key); +} + int cmd_clone(int argc, const char **argv, const char *prefix) { int use_local_hardlinks = 1; @@ -539,6 +552,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) head_points_at = NULL; remote_head = NULL; option_no_checkout = 1; + if (!option_bare) + install_branch_config("master", option_origin, + "refs/heads/master"); } if (head_points_at) { @@ -567,11 +583,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) head_points_at->peer_ref->name, reflog_msg.buf); - strbuf_addf(&key, "branch.%s.remote", head); - git_config_set(key.buf, option_origin); - strbuf_reset(&key); - strbuf_addf(&key, "branch.%s.merge", head); - git_config_set(key.buf, head_points_at->name); + install_branch_config(head, option_origin, + head_points_at->name); } } else if (remote_head) { /* Source had detached HEAD pointing somewhere. */ diff --git a/builtin-gc.c b/builtin-gc.c index a2014388da..8d990ed493 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -161,7 +161,8 @@ static int need_to_gc(void) */ if (too_many_packs()) append_option(argv_repack, - !strcmp(prune_expire, "now") ? "-a" : "-A", + prune_expire && !strcmp(prune_expire, "now") ? + "-a" : "-A", MAX_ADD); else if (!too_many_loose_objects()) return 0; @@ -173,14 +174,15 @@ static int need_to_gc(void) int cmd_gc(int argc, const char **argv, const char *prefix) { - int prune = 0; int aggressive = 0; int auto_gc = 0; int quiet = 0; char buf[80]; struct option builtin_gc_options[] = { - OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects (deprecated)"), + { OPTION_STRING, 0, "prune", &prune_expire, "date", + "prune unreferenced objects", + PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire }, OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"), OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"), OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"), @@ -218,7 +220,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix) "\"git help gc\" for more information.\n"); } else append_option(argv_repack, - !strcmp(prune_expire, "now") ? "-a" : "-A", + prune_expire && !strcmp(prune_expire, "now") + ? "-a" : "-A", MAX_ADD); if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD)) @@ -230,9 +233,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix) if (run_command_v_opt(argv_repack, RUN_GIT_CMD)) return error(FAILED_RUN, argv_repack[0]); - argv_prune[2] = prune_expire; - if (run_command_v_opt(argv_prune, RUN_GIT_CMD)) - return error(FAILED_RUN, argv_prune[0]); + if (prune_expire) { + argv_prune[2] = prune_expire; + if (run_command_v_opt(argv_prune, RUN_GIT_CMD)) + return error(FAILED_RUN, argv_prune[0]); + } if (run_command_v_opt(argv_rerere, RUN_GIT_CMD)) return error(FAILED_RUN, argv_rerere[0]); diff --git a/builtin-merge.c b/builtin-merge.c index 885fad9bba..6d2160d0a3 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -356,9 +356,14 @@ static void merge_name(const char *remote, struct strbuf *msg) struct object *remote_head; unsigned char branch_head[20], buf_sha[20]; struct strbuf buf = STRBUF_INIT; + struct strbuf bname = STRBUF_INIT; const char *ptr; int len, early; + len = strlen(remote); + if (interpret_nth_last_branch(remote, &bname) == len) + remote = bname.buf; + memset(branch_head, 0, sizeof(branch_head)); remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT); if (!remote_head) @@ -371,7 +376,7 @@ static void merge_name(const char *remote, struct strbuf *msg) if (!hashcmp(remote_head->sha1, branch_head)) { strbuf_addf(msg, "%s\t\tbranch '%s' of .\n", sha1_to_hex(branch_head), remote); - return; + goto cleanup; } /* See if remote matches <name>^^^.. or <name>~<number> */ @@ -411,7 +416,8 @@ static void merge_name(const char *remote, struct strbuf *msg) sha1_to_hex(remote_head->sha1), truname.buf + 11, (early ? " (early part)" : "")); - return; + strbuf_release(&truname); + goto cleanup; } } @@ -432,10 +438,13 @@ static void merge_name(const char *remote, struct strbuf *msg) strbuf_remove(&line, ptr-line.buf+1, 13); strbuf_addbuf(msg, &line); strbuf_release(&line); - return; + goto cleanup; } strbuf_addf(msg, "%s\t\tcommit '%s'\n", sha1_to_hex(remote_head->sha1), remote); +cleanup: + strbuf_release(&buf); + strbuf_release(&bname); } static int git_merge_config(const char *k, const char *v, void *cb) 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 f7e04c45f9..a970b39505 100644 --- a/builtin-receive-pack.c +++ b/builtin-receive-pack.c @@ -244,7 +244,7 @@ static void warn_unconfigured_deny(void) { int i; for (i = 0; i < ARRAY_SIZE(warn_unconfigured_deny_msg); i++) - warning(warn_unconfigured_deny_msg[i]); + warning("%s", warn_unconfigured_deny_msg[i]); } static char *warn_unconfigured_deny_delete_current_msg[] = { @@ -270,7 +270,7 @@ static void warn_unconfigured_deny_delete_current(void) for (i = 0; i < ARRAY_SIZE(warn_unconfigured_deny_delete_current_msg); i++) - warning(warn_unconfigured_deny_delete_current_msg[i]); + warning("%s", warn_unconfigured_deny_delete_current_msg[i]); } static const char *update(struct command *cmd) @@ -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-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-shortlog.c b/builtin-shortlog.c index 5f9f3f09b1..badd912038 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -40,6 +40,7 @@ static void insert_one_record(struct shortlog *log, char *buffer, *p; struct string_list_item *item; char namebuf[1024]; + char emailbuf[1024]; size_t len; const char *eol; const char *boemail, *eoemail; @@ -51,7 +52,19 @@ static void insert_one_record(struct shortlog *log, eoemail = strchr(boemail, '>'); if (!eoemail) return; - if (!map_email(&log->mailmap, boemail+1, namebuf, sizeof(namebuf))) { + + /* copy author name to namebuf, to support matching on both name and email */ + memcpy(namebuf, author, boemail - author); + len = boemail - author; + while(len > 0 && isspace(namebuf[len-1])) + len--; + namebuf[len] = 0; + + /* copy email name to emailbuf, to allow email replacement as well */ + memcpy(emailbuf, boemail+1, eoemail - boemail); + emailbuf[eoemail - boemail - 1] = 0; + + if (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) { while (author < boemail && isspace(*author)) author++; for (len = 0; @@ -67,8 +80,8 @@ static void insert_one_record(struct shortlog *log, if (log->email) { size_t room = sizeof(namebuf) - len - 1; - int maillen = eoemail - boemail + 1; - snprintf(namebuf + len, room, " %.*s", maillen, boemail); + int maillen = strlen(emailbuf); + snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf); } item = string_list_insert(namebuf, &log->list); @@ -219,7 +232,7 @@ void shortlog_init(struct shortlog *log) { memset(log, 0, sizeof(*log)); - read_mailmap(&log->mailmap, ".mailmap", &log->common_repo_prefix); + read_mailmap(&log->mailmap, &log->common_repo_prefix); log->list.strdup_strings = 1; log->wrap = DEFAULT_WRAPLEN; @@ -248,6 +261,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) struct parse_opt_ctx_t ctx; prefix = setup_git_directory_gently(&nongit); + git_config(git_default_config, NULL); shortlog_init(&log); init_revisions(&rev, prefix); parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH | @@ -320,6 +334,5 @@ void shortlog_output(struct shortlog *log) log->list.strdup_strings = 1; string_list_clear(&log->list, 1); - log->mailmap.strdup_strings = 1; - string_list_clear(&log->mailmap, 1); + clear_mailmap(&log->mailmap); } diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index cafc4eba7c..6ae6bcc0e8 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -45,8 +45,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) break; case 2: if (!strcmp(argv[0], "HEAD") && - prefixcmp(argv[1], "refs/heads/")) - die("Refusing to point HEAD outside of refs/heads/"); + prefixcmp(argv[1], "refs/")) + die("Refusing to point HEAD outside of refs/"); create_symref(argv[0], argv[1], msg); break; default: diff --git a/builtin-update-index.c b/builtin-update-index.c index 5604977505..dd43d5bef4 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -742,8 +742,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) if (newfd < 0) { if (refresh_flags & REFRESH_QUIET) exit(128); - die("unable to create '%s.lock': %s", - get_index_file(), strerror(lock_error)); + unable_to_lock_index_die(get_index_file(), lock_error); } if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(lock_file)) 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; @@ -484,6 +484,7 @@ struct lock_file { }; #define LOCK_DIE_ON_ERROR 1 #define LOCK_NODEREF 2 +extern NORETURN void unable_to_lock_index_die(const char *path, int err); extern int hold_lock_file_for_update(struct lock_file *, const char *path, int); extern int hold_lock_file_for_append(struct lock_file *, const char *path, int); extern int commit_lock_file(struct lock_file *); @@ -626,6 +627,7 @@ const char *make_nonrelative_path(const char *path); const char *make_relative_path(const char *abs, const char *base); int normalize_path_copy(char *dst, const char *src); int longest_ancestor_length(const char *path, const char *prefix_list); +char *strip_path_suffix(const char *path, const char *suffix); /* Read and unpack a sha1 file into memory, write memory to a sha1 file */ extern int sha1_object_info(const unsigned char *, unsigned long *); @@ -694,7 +696,8 @@ enum date_mode { DATE_SHORT, DATE_LOCAL, DATE_ISO8601, - DATE_RFC2822 + DATE_RFC2822, + DATE_RAW }; const char *show_date(unsigned long time, int timezone, enum date_mode mode); @@ -868,6 +871,7 @@ extern int user_ident_explicitly_given; extern const char *git_commit_encoding; extern const char *git_log_output_encoding; +extern const char *git_mailmap_file; /* IO helper functions */ extern void maybe_flush_or_die(FILE *, const char *); @@ -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/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..cb9c4d4dd5 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 stat64(x,y) mingw_lstat(x,y) int mingw_utime(const char *file_name, const struct utimbuf *times); #define utime mingw_utime @@ -565,6 +565,15 @@ static int git_default_branch_config(const char *var, const char *value) return 0; } +static int git_default_mailmap_config(const char *var, const char *value) +{ + if (!strcmp(var, "mailmap.file")) + return git_config_string(&git_mailmap_file, var, value); + + /* Add other config variables here and to Documentation/config.txt. */ + return 0; +} + int git_default_config(const char *var, const char *value, void *dummy) { if (!prefixcmp(var, "core.")) @@ -579,6 +588,9 @@ int git_default_config(const char *var, const char *value, void *dummy) if (!prefixcmp(var, "branch.")) return git_default_branch_config(var, value); + if (!prefixcmp(var, "mailmap.")) + return git_default_mailmap_config(var, value); + if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) { pager_use_color = git_config_bool(var,value); return 0; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f44f63cfeb..90ba428e94 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 @@ -385,7 +389,7 @@ __git_complete_revlist () __git_all_commands () { - if [ -n "$__git_all_commandlist" ]; then + if [ -n "${__git_all_commandlist-}" ]; then echo "$__git_all_commandlist" return fi @@ -403,7 +407,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 @@ -975,6 +979,27 @@ _git_ls_tree () __git_complete_file } +# Options that go well for log, shortlog and gitk +__git_log_common_options=" + --not --all + --branches --tags --remotes + --first-parent --no-merges + --max-count= + --max-age= --since= --after= + --min-age= --until= --before= +" +# Options that go well for log and gitk (not shortlog) +__git_log_gitk_options=" + --dense --sparse --full-history + --simplify-merges --simplify-by-decoration + --left-right +" +# Options that go well for log and shortlog (not gitk) +__git_log_shortlog_options=" + --author= --committer= --grep= + --all-match +" + __git_log_pretty_formats="oneline short medium full fuller email raw format:" _git_log () @@ -982,6 +1007,11 @@ _git_log () __git_has_doubledash && return local cur="${COMP_WORDS[COMP_CWORD]}" + local g="$(git rev-parse --git-dir 2>/dev/null)" + local merge="" + if [ -f $g/MERGE_HEAD ]; then + merge="--merge" + fi case "$cur" in --pretty=*) __gitcomp "$__git_log_pretty_formats @@ -996,22 +1026,20 @@ _git_log () ;; --*) __gitcomp " - --max-count= --max-age= --since= --after= - --min-age= --before= --until= + $__git_log_common_options + $__git_log_shortlog_options + $__git_log_gitk_options --root --topo-order --date-order --reverse - --no-merges --follow + --follow --abbrev-commit --abbrev= --relative-date --date= - --author= --committer= --grep= - --all-match --pretty= - --not --all - --left-right --cherry-pick + --cherry-pick --graph --decorate --walk-reflogs - --parents --children --full-history - --merge + --parents --children + $merge $__git_diff_common_options --pickaxe-all --pickaxe-regex " @@ -1037,6 +1065,7 @@ _git_merge () --*) __gitcomp " --no-commit --no-stat --log --no-log --squash --strategy + --commit --stat --no-squash --ff --no-ff " return esac @@ -1196,10 +1225,14 @@ _git_config () __gitcomp "$(__git_merge_strategies)" return ;; - color.branch|color.diff|color.status) + color.branch|color.diff|color.interactive|color.status|color.ui) __gitcomp "always never auto" return ;; + color.pager) + __gitcomp "false true" + return + ;; color.*.*) __gitcomp " normal black red green yellow blue magenta cyan white @@ -1491,12 +1524,8 @@ _git_shortlog () case "$cur" in --*) __gitcomp " - --max-count= --max-age= --since= --after= - --min-age= --before= --until= - --no-merges - --author= --committer= --grep= - --all-match - --not --all + $__git_log_common_options + $__git_log_shortlog_options --numbered --summary " return @@ -1595,7 +1624,8 @@ _git_svn () local subcommands=" init fetch clone rebase dcommit log find-rev set-tree commit-diff info create-ignore propget - proplist show-ignore show-externals + proplist show-ignore show-externals branch tag blame + migrate " local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then @@ -1606,13 +1636,15 @@ _git_svn () --follow-parent --authors-file= --repack= --no-metadata --use-svm-props --use-svnsync-props --log-window-size= --no-checkout --quiet - --repack-flags --user-log-author --localtime $remote_opts + --repack-flags --use-log-author --localtime + --ignore-paths= $remote_opts " local init_opts=" --template= --shared= --trunk= --tags= --branches= --stdlayout --minimize-url --no-metadata --use-svm-props --use-svnsync-props - --rewrite-root= $remote_opts + --rewrite-root= --prefix= --use-log-author + --add-author-from $remote_opts " local cmt_opts=" --edit --rmdir --find-copies-harder --copy-similarity= @@ -1632,7 +1664,8 @@ _git_svn () dcommit,--*) __gitcomp " --merge --strategy= --verbose --dry-run - --fetch-all --no-rebase $cmt_opts $fc_opts + --fetch-all --no-rebase --commit-url + --revision $cmt_opts $fc_opts " ;; set-tree,--*) @@ -1646,13 +1679,13 @@ _git_svn () __gitcomp " --limit= --revision= --verbose --incremental --oneline --show-commit --non-recursive - --authors-file= + --authors-file= --color " ;; rebase,--*) __gitcomp " --merge --verbose --strategy= --local - --fetch-all $fc_opts + --fetch-all --dry-run $fc_opts " ;; commit-diff,--*) @@ -1661,6 +1694,21 @@ _git_svn () info,--*) __gitcomp "--url" ;; + branch,--*) + __gitcomp "--dry-run --message --tag" + ;; + tag,--*) + __gitcomp "--dry-run --message" + ;; + blame,--*) + __gitcomp "--git-format" + ;; + migrate,--*) + __gitcomp " + --config-dir= --ignore-paths= --minimize + --no-auth-cache --username= + " + ;; *) COMPREPLY=() ;; @@ -1797,14 +1845,18 @@ _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" fi case "$cur" in --*) - __gitcomp "--not --all $merge" + __gitcomp " + $__git_log_common_options + $__git_log_gitk_options + $merge + " return ;; esac diff --git a/contrib/emacs/README b/contrib/emacs/README new file mode 100644 index 0000000000..82368bdbff --- /dev/null +++ b/contrib/emacs/README @@ -0,0 +1,39 @@ +This directory contains various modules for Emacs support. + +To make the modules available to Emacs, you should add this directory +to your load-path, and then require the modules you want. This can be +done by adding to your .emacs something like this: + + (add-to-list 'load-path ".../git/contrib/emacs") + (require 'git) + (require 'git-blame) + + +The following modules are available: + +* git.el: + + Status manager that displays the state of all the files of the + project, and provides easy access to the most frequently used git + commands. The user interface is as far as possible compatible with + the pcl-cvs mode. It can be started with `M-x git-status'. + +* git-blame.el: + + Emacs implementation of incremental git-blame. When you turn it on + while viewing a file, the editor buffer will be updated by setting + the background of individual lines to a color that reflects which + commit it comes from. And when you move around the buffer, a + one-line summary will be shown in the echo area. + +* vc-git.el: + + This file used to contain the VC-mode backend for git, but it is no + longer distributed with git. It is now maintained as part of Emacs + and included in standard Emacs distributions starting from version + 22.2. + + If you have an earlier Emacs version, upgrading to Emacs 22 is + recommended, since the VC mode in older Emacs is not generic enough + to be able to support git in a reasonable manner, and no attempt has + been made to backport vc-git.el. diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index fcbe2d9cf5..eace9c18eb 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -530,9 +530,9 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (git-fileinfo->needs-refresh info) t))) (defun git-status-filenames-map (status func files &rest args) - "Apply FUNC to the status files names in the FILES list." + "Apply FUNC to the status files names in the FILES list. +The list must be sorted." (when files - (setq files (sort files #'string-lessp)) (let ((file (pop files)) (node (ewoc-nth status 0))) (while (and file node) @@ -545,7 +545,7 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (setq file (pop files)))))))) (defun git-set-filenames-state (status files state) - "Set the state of a list of named files." + "Set the state of a list of named files. The list must be sorted" (when files (git-status-filenames-map status #'git-set-fileinfo-state files state) (unless state ;; delete files whose state has been set to nil @@ -750,6 +750,7 @@ Return the list of files that haven't been handled." (let (unmerged-files) (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) (push (match-string 1) unmerged-files)) + (setq unmerged-files (nreverse unmerged-files)) ;; assume it is sorted already (git-set-filenames-state status unmerged-files 'unmerged)))) (defun git-get-exclude-files () @@ -770,17 +771,18 @@ Return the list of files that haven't been handled." (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) (defun git-update-status-files (&optional files mark-files) - "Update the status of FILES from the index." + "Update the status of FILES from the index. +The FILES list must be sorted." (unless git-status (error "Not in git-status buffer.")) ;; set the needs-update flag on existing files - (if (setq files (sort files #'string-lessp)) + (if files (git-status-filenames-map git-status (lambda (info) (setf (git-fileinfo->needs-update info) t)) files) (ewoc-map (lambda (info) (setf (git-fileinfo->needs-update info) t) nil) git-status) (git-call-process nil "update-index" "--refresh") (when git-show-uptodate (git-run-ls-files-cached git-status nil 'uptodate))) - (let* ((remaining-files + (let ((remaining-files (if (git-empty-db-p) ; we need some special handling for an empty db (git-run-ls-files-cached git-status files 'added) (git-run-diff-index git-status files)))) @@ -825,13 +827,13 @@ Return the list of files that haven't been handled." (list (ewoc-data (ewoc-locate git-status))))) (defun git-marked-files-state (&rest states) - "Return marked files that are in the specified states." + "Return a sorted list of marked files that are in the specified states." (let ((files (git-marked-files)) result) (dolist (info files) (when (memq (git-fileinfo->state info) states) (push info result))) - result)) + (nreverse result))) (defun git-refresh-files () "Refresh all files that need it and clear the needs-refresh flag." @@ -1066,7 +1068,9 @@ Return the list of files that haven't been handled." (unless files (push (file-relative-name (read-file-name "File to remove: " nil nil t)) files)) (if (yes-or-no-p - (format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" ""))) + (if (cdr files) + (format "Remove %d files? " (length files)) + (format "Remove %s? " (car files)))) (progn (dolist (name files) (ignore-errors @@ -1085,7 +1089,9 @@ Return the list of files that haven't been handled." added modified) (when (and files (yes-or-no-p - (format "Revert %d file%s? " (length files) (if (> (length files) 1) "s" "")))) + (if (cdr files) + (format "Revert %d files? " (length files)) + (format "Revert %s? " (git-fileinfo->name (car files)))))) (dolist (info files) (case (git-fileinfo->state info) ('added (push (git-fileinfo->name info) added)) @@ -1101,13 +1107,14 @@ Return the list of files that haven't been handled." (or (not added) (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added)) (or (not modified) - (apply 'git-call-process-display-error "checkout" "HEAD" modified))))) - (git-update-status-files (append added modified)) + (apply 'git-call-process-display-error "checkout" "HEAD" modified)))) + (names (git-get-filenames files))) + (git-update-status-files names) (when ok (dolist (file modified) (let ((buffer (get-file-buffer file))) (when buffer (with-current-buffer buffer (revert-buffer t t t))))) - (git-success-message "Reverted" (git-get-filenames files))))))) + (git-success-message "Reverted" names)))))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." @@ -1365,14 +1372,14 @@ Return the list of files that haven't been handled." (mapconcat #'identity msg "\n")))) (defun git-get-commit-files (commit) - "Retrieve the list of files modified by COMMIT." + "Retrieve a sorted list of files modified by COMMIT." (let (files) (with-temp-buffer (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" "--root" commit) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (push (match-string 1) files))) - files)) + (sort files #'string-lessp))) (defun git-read-commit-name (prompt &optional default) "Ask for a commit name, with completion for local branch, remote branch and tag." 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..3832f60225 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): @@ -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; } @@ -89,6 +89,11 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode) struct tm *tm; static char timebuf[200]; + if (mode == DATE_RAW) { + snprintf(timebuf, sizeof(timebuf), "%lu %+05d", time, tz); + return timebuf; + } + if (mode == DATE_RELATIVE) { unsigned long diff; struct timeval now; @@ -615,6 +620,8 @@ enum date_mode parse_date_format(const char *format) return DATE_LOCAL; else if (!strcmp(format, "default")) return DATE_NORMAL; + else if (!strcmp(format, "raw")) + return DATE_RAW; else die("unknown date format %s", format); } diff --git a/diff-no-index.c b/diff-no-index.c index 0dbd9dad8b..0a14268ba9 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -247,6 +247,7 @@ void diff_no_index(struct rev_info *revs, else revs->diffopt.paths = argv + argc - 2; revs->diffopt.nr_paths = 2; + revs->diffopt.skip_stat_unmatch = 1; DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS); DIFF_OPT_SET(&revs->diffopt, NO_INDEX); @@ -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); @@ -184,11 +184,11 @@ static int remove_tempfile_installed; static void remove_tempfile(void) { int i; - for (i = 0; i < ARRAY_SIZE(diff_temp); i++) - if (diff_temp[i].name == diff_temp[i].tmp_path) { + for (i = 0; i < ARRAY_SIZE(diff_temp); i++) { + if (diff_temp[i].name == diff_temp[i].tmp_path) unlink(diff_temp[i].name); - diff_temp[i].name = NULL; - } + diff_temp[i].name = NULL; + } } static void remove_tempfile_on_signal(int signo) @@ -2326,15 +2326,12 @@ void diff_setup(struct diff_options *options) options->break_opt = -1; options->rename_limit = -1; options->dirstat_percent = 3; - DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE); options->context = 3; options->change = diff_change; options->add_remove = diff_addremove; if (diff_use_color_default > 0) DIFF_OPT_SET(options, COLOR_DIFF); - else - DIFF_OPT_CLR(options, COLOR_DIFF); options->detect_rename = diff_detect_rename_default; if (!diff_mnemonic_prefix) { @@ -2570,13 +2567,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")) { @@ -2597,10 +2594,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; @@ -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/exec_cmd.c b/exec_cmd.c index f234066def..217c12577f 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -23,35 +23,10 @@ const char *system_path(const char *path) assert(argv0_path); assert(is_absolute_path(argv0_path)); - if (!prefix) { - const char *strip[] = { - GIT_EXEC_PATH, - BINDIR, - 0 - }; - const char **s; - - for (s = strip; *s; s++) { - const char *sargv = argv0_path + strlen(argv0_path); - const char *ss = *s + strlen(*s); - while (argv0_path < sargv && *s < ss - && (*sargv == *ss || - (is_dir_sep(*sargv) && is_dir_sep(*ss)))) { - sargv--; - ss--; - } - if (*s == ss) { - struct strbuf d = STRBUF_INIT; - /* We also skip the trailing directory separator. */ - assert(sargv - argv0_path - 1 >= 0); - strbuf_add(&d, argv0_path, sargv - argv0_path - 1); - prefix = strbuf_detach(&d, NULL); - break; - } - } - } - - if (!prefix) { + if (!prefix && + !(prefix = strip_path_suffix(argv0_path, GIT_EXEC_PATH)) && + !(prefix = strip_path_suffix(argv0_path, BINDIR)) && + !(prefix = strip_path_suffix(argv0_path, "git"))) { prefix = PREFIX; fprintf(stderr, "RUNTIME_PREFIX requested, " "but prefix computation failed. " diff --git a/fast-import.c b/fast-import.c index 3ef3413e69..3748ddf48d 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)); diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 5f129a4203..f7b0761732 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -753,6 +753,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..dcf4127750 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) { diff --git a/git-filter-branch.sh b/git-filter-branch.sh index 27b57b826a..20f6f51750 100755 --- a/git-filter-branch.sh +++ b/git-filter-branch.sh @@ -220,13 +220,21 @@ die "" # Remove tempdir on exit trap 'cd ../..; rm -rf "$tempdir"' 0 +ORIG_GIT_DIR="$GIT_DIR" +ORIG_GIT_WORK_TREE="$GIT_WORK_TREE" +ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE" +GIT_WORK_TREE=. +export GIT_DIR GIT_WORK_TREE + # Make sure refs/original is empty git for-each-ref > "$tempdir"/backup-refs || exit 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 @@ -234,12 +242,6 @@ do esac done < "$tempdir"/backup-refs -ORIG_GIT_DIR="$GIT_DIR" -ORIG_GIT_WORK_TREE="$GIT_WORK_TREE" -ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE" -GIT_WORK_TREE=. -export GIT_DIR GIT_WORK_TREE - # The refs should be updated if their heads were rewritten git rev-parse --no-flags --revs-only --symbolic-full-name \ --default HEAD "$@" > "$tempdir"/raw-heads || exit diff --git a/git-pull.sh b/git-pull.sh index 2c7f432dc0..25adddfddf 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -171,6 +171,11 @@ case "$merge_head" in echo >&2 "Cannot merge multiple branches into empty head" exit 1 fi + if test true = "$rebase" + then + echo >&2 "Cannot rebase onto multiple branches" + exit 1 + fi ;; esac 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 6d3eddbada..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 ;; @@ -310,6 +319,7 @@ do esac shift done +test $# -gt 2 && usage # Make sure we do not have $GIT_DIR/rebase-apply if test -z "$do_merge" @@ -419,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-sh-setup.sh b/git-sh-setup.sh index 2142308bcc..838233926f 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -85,27 +85,14 @@ cd_to_toplevel () { cdup=$(git rev-parse --show-cdup) if test ! -z "$cdup" then - case "$cdup" in - /*) - # Not quite the same as if we did "cd -P '$cdup'" when - # $cdup contains ".." after symlink path components. - # Don't fix that case at least until Git switches to - # "cd -P" across the board. - phys="$cdup" - ;; - ..|../*|*/..|*/../*) - # Interpret $cdup relative to the physical, not logical, cwd. - # Probably /bin/pwd is more portable than passing -P to cd or pwd. - phys="$(unset PWD; /bin/pwd)/$cdup" - ;; - *) - # There's no "..", so no need to make things absolute. - phys="$cdup" - ;; - esac - - cd "$phys" || { - echo >&2 "Cannot chdir to $phys, the toplevel of the working tree" + # The "-P" option says to follow "physical" directory + # structure instead of following symbolic links. When cdup is + # "../", this means following the ".." entry in the current + # directory instead textually removing a symlink path element + # from the PWD shell variable. The "-P" behavior is more + # consistent with the C-style chdir used by most of Git. + cd -P "$cdup" || { + echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree" exit 1 } fi diff --git a/git-submodule.sh b/git-submodule.sh index 6cc2d334c0..204aab671e 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -5,7 +5,7 @@ # Copyright (c) 2007 Lars Hjemli USAGE="[--quiet] [--cached] \ -[add <repo> [-b branch] <path>]|[status|init|update [-i|--init]|summary [-n|--summary-limit <n>] [<commit>]] \ +[add <repo> [-b branch] <path>]|[status|init|update [-i|--init] [-N|--no-fetch]|summary [-n|--summary-limit <n>] [<commit>]] \ [--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]" OPTIONS_SPEC= . git-sh-setup @@ -16,6 +16,7 @@ command= branch= quiet= cached= +nofetch= # # print stuff on stdout unless -q was specified @@ -300,6 +301,10 @@ cmd_update() shift cmd_init "$@" || return ;; + -N|--no-fetch) + shift + nofetch=1 + ;; --) shift break @@ -345,8 +350,16 @@ cmd_update() then force="-f" fi - (unset GIT_DIR; cd "$path" && git-fetch && - git-checkout $force -q "$sha1") || + + if test -z "$nofetch" + then + (unset GIT_DIR; cd "$path" && + git-fetch) || + die "Unable to fetch in submodule path '$path'" + fi + + (unset GIT_DIR; cd "$path" && + git-checkout $force -q "$sha1") || die "Unable to checkout '$sha1' in submodule path '$path'" say "Submodule path '$path': checked out '$sha1'" diff --git a/git-svn.perl b/git-svn.perl index 001a1d8eff..959eb52f3f 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: $!"); @@ -1693,6 +1707,7 @@ sub find_by_url { # repos_root and, path are optional my $prefix = ''; if ($rwr) { $z = $rwr; + remove_username($z); } elsif (defined $svm) { $z = $svm->{source}; $prefix = $svm->{replace}; @@ -2416,6 +2431,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, @@ -2525,7 +2541,7 @@ sub get_untracked { sub parse_svn_date { my $date = shift || return '+0000 1970-01-01 00:00:00'; my ($Y,$m,$d,$H,$M,$S) = ($date =~ /^(\d{4})\-(\d\d)\-(\d\d)T - (\d\d)\:(\d\d)\:(\d\d).\d+Z$/x) or + (\d\d)\:(\d\d)\:(\d\d)\.\d*Z$/x) or croak "Unable to parse date: $date\n"; my $parsed_date; # Set next. @@ -3281,7 +3297,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; @@ -4614,6 +4630,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/; @@ -4720,7 +4737,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/gitweb/gitweb.perl b/gitweb/gitweb.perl index 5410874285..33ef190ceb 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -27,13 +27,29 @@ our $version = "++GIT_VERSION++"; our $my_url = $cgi->url(); our $my_uri = $cgi->url(-absolute => 1); -# if we're called with PATH_INFO, we have to strip that -# from the URL to find our real URL -# we make $path_info global because it's also used later on +# Base URL for relative URLs in gitweb ($logo, $favicon, ...), +# needed and used only for URLs with nonempty PATH_INFO +our $base_url = $my_url; + +# When the script is used as DirectoryIndex, the URL does not contain the name +# of the script file itself, and $cgi->url() fails to strip PATH_INFO, so we +# have to do it ourselves. We make $path_info global because it's also used +# later on. +# +# Another issue with the script being the DirectoryIndex is that the resulting +# $my_url data is not the full script URL: this is good, because we want +# generated links to keep implying the script name if it wasn't explicitly +# indicated in the URL we're handling, but it means that $my_url cannot be used +# as base URL. +# Therefore, if we needed to strip PATH_INFO, then we know that we have +# to build the base URL ourselves: our $path_info = $ENV{"PATH_INFO"}; if ($path_info) { - $my_url =~ s,\Q$path_info\E$,,; - $my_uri =~ s,\Q$path_info\E$,,; + if ($my_url =~ s,\Q$path_info\E$,, && + $my_uri =~ s,\Q$path_info\E$,, && + defined $ENV{'SCRIPT_NAME'}) { + $base_url = $cgi->url(-base => 1) . $ENV{'SCRIPT_NAME'}; + } } # core git executable to use @@ -386,13 +402,13 @@ sub feature_bool { my $key = shift; my ($val) = git_get_project_config($key, '--bool'); - if ($val eq 'true') { + if (!defined $val) { + return ($_[0]); + } elsif ($val eq 'true') { return (1); } elsif ($val eq 'false') { return (0); } - - return ($_[0]); } sub feature_snapshot { @@ -1368,13 +1384,11 @@ sub format_log_line_html { my $line = shift; $line = esc_html($line, -nbsp=>1); - if ($line =~ m/([0-9a-fA-F]{8,40})/) { - my $hash_text = $1; - my $link = - $cgi->a({-href => href(action=>"object", hash=>$hash_text), - -class => "text"}, $hash_text); - $line =~ s/$hash_text/$link/; - } + $line =~ s{\b([0-9a-fA-F]{8,40})\b}{ + $cgi->a({-href => href(action=>"object", hash=>$1), + -class => "text"}, $1); + }eg; + return $line; } @@ -1898,18 +1912,19 @@ sub git_parse_project_config { return %config; } -# convert config value to boolean, 'true' or 'false' +# convert config value to boolean: 'true' or 'false' # no value, number > 0, 'true' and 'yes' values are true # rest of values are treated as false (never as error) sub config_to_bool { my $val = shift; + return 1 if !defined $val; # section.key + # strip leading and trailing whitespace $val =~ s/^\s+//; $val =~ s/\s+$//; - return (!defined $val || # section.key - ($val =~ /^\d+$/ && $val) || # section.key = 1 + return (($val =~ /^\d+$/ && $val) || # section.key = 1 ($val =~ /^(?:true|yes)$/i)); # section.key = true } @@ -1962,6 +1977,9 @@ sub git_get_project_config { $config_file = "$git_dir/config"; } + # check if config variable (key) exists + return unless exists $config{"gitweb.$key"}; + # ensure given type if (!defined $type) { return $config{"gitweb.$key"}; @@ -2908,7 +2926,7 @@ EOF # the stylesheet, favicon etc urls won't work correctly with path_info # unless we set the appropriate base URL if ($ENV{'PATH_INFO'}) { - print '<base href="'.esc_url($my_url).'" />\n'; + print "<base href=\"".esc_url($base_url)."\" />\n"; } # print out each stylesheet that exist, providing backwards capability # for those people who defined $stylesheet in a config file diff --git a/http-push.c b/http-push.c index a8ae545dfb..30d2d34041 100644 --- a/http-push.c +++ b/http-push.c @@ -153,6 +153,7 @@ struct remote_lock char *url; char *owner; char *token; + char tmpfile_suffix[41]; time_t start_time; long timeout; int refreshing; @@ -557,8 +558,7 @@ static void start_put(struct transfer_request *request) request->dest = strbuf_detach(&buf, NULL); append_remote_object_url(&buf, remote->url, hex, 0); - strbuf_addstr(&buf, "_"); - strbuf_addstr(&buf, request->lock->token); + strbuf_add(&buf, request->lock->tmpfile_suffix, 41); request->url = strbuf_detach(&buf, NULL); slot = get_active_slot(); @@ -1130,6 +1130,8 @@ static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed) static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed) { struct remote_lock *lock = (struct remote_lock *)ctx->userData; + git_SHA_CTX sha_ctx; + unsigned char lock_token_sha1[20]; if (tag_closed && ctx->cdata) { if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) { @@ -1142,6 +1144,13 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed) } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) { lock->token = xmalloc(strlen(ctx->cdata) + 1); strcpy(lock->token, ctx->cdata); + + git_SHA1_Init(&sha_ctx); + git_SHA1_Update(&sha_ctx, lock->token, strlen(lock->token)); + git_SHA1_Final(lock_token_sha1, &sha_ctx); + + lock->tmpfile_suffix[0] = '_'; + memcpy(lock->tmpfile_suffix + 1, sha1_to_hex(lock_token_sha1), 40); } } } 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 021c3375c1..3dbb2d1ff9 100644 --- a/lockfile.c +++ b/lockfile.c @@ -155,11 +155,25 @@ static int lock_file(struct lock_file *lk, const char *path, int flags) return lk->fd; } + +NORETURN void unable_to_lock_index_die(const char *path, int err) +{ + 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" + "process is running and remove the file manually to continue.", + path, strerror(err)); + } else { + die("Unable to create '%s.lock': %s", path, strerror(err)); + } +} + int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags) { int fd = lock_file(lk, path, flags); if (fd < 0 && (flags & LOCK_DIE_ON_ERROR)) - die("unable to create '%s.lock': %s", path, strerror(errno)); + unable_to_lock_index_die(path, errno); return fd; } @@ -170,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 194ddb13da..84a74e544b 100644 --- a/log-tree.c +++ b/log-tree.c @@ -48,7 +48,7 @@ static void show_parents(struct commit *commit, int abbrev) struct commit_list *p; for (p = commit->parents; p ; p = p->next) { struct commit *parent = p->item; - printf(" %s", diff_unique_abbrev(parent->object.sha1, abbrev)); + printf(" %s", find_unique_abbrev(parent->object.sha1, abbrev)); } } @@ -280,7 +280,7 @@ void show_log(struct rev_info *opt) putchar('>'); } } - fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); + fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); if (opt->print_parents) show_parents(commit, abbrev_commit); show_decorations(opt, commit); @@ -348,13 +348,13 @@ void show_log(struct rev_info *opt) putchar('>'); } } - fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), + fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); if (opt->print_parents) show_parents(commit, abbrev_commit); if (parent) printf(" (from %s)", - diff_unique_abbrev(parent->object.sha1, + find_unique_abbrev(parent->object.sha1, abbrev_commit)); show_decorations(opt, commit); printf("%s", diff_get_color_opt(&opt->diffopt, DIFF_RESET)); @@ -2,17 +2,131 @@ #include "string-list.h" #include "mailmap.h" -int read_mailmap(struct string_list *map, const char *filename, char **repo_abbrev) +#define DEBUG_MAILMAP 0 +#if DEBUG_MAILMAP +#define debug_mm(...) fprintf(stderr, __VA_ARGS__) +#else +static inline void debug_mm(const char *format, ...) {} +#endif + +const char *git_mailmap_file; + +struct mailmap_info { + char *name; + char *email; +}; + +struct mailmap_entry { + /* name and email for the simple mail-only case */ + char *name; + char *email; + + /* name and email for the complex mail and name matching case */ + struct string_list namemap; +}; + +static void free_mailmap_info(void *p, const char *s) +{ + struct mailmap_info *mi = (struct mailmap_info *)p; + debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n", s, mi->name, mi->email); + free(mi->name); + free(mi->email); +} + +static void free_mailmap_entry(void *p, const char *s) +{ + struct mailmap_entry *me = (struct mailmap_entry *)p; + debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n", s, me->namemap.nr); + debug_mm("mailmap: - simple: '%s' <%s>\n", me->name, me->email); + free(me->name); + free(me->email); + + me->namemap.strdup_strings = 1; + string_list_clear_func(&me->namemap, free_mailmap_info); +} + +static void add_mapping(struct string_list *map, + char *new_name, char *new_email, char *old_name, char *old_email) +{ + struct mailmap_entry *me; + int index; + if (old_email == NULL) { + old_email = new_email; + new_email = NULL; + } + + if ((index = string_list_find_insert_index(map, old_email, 1)) < 0) { + /* mailmap entry exists, invert index value */ + index = -1 - index; + } else { + /* create mailmap entry */ + struct string_list_item *item = string_list_insert_at_index(index, old_email, map); + item->util = xmalloc(sizeof(struct mailmap_entry)); + memset(item->util, 0, sizeof(struct mailmap_entry)); + ((struct mailmap_entry *)item->util)->namemap.strdup_strings = 1; + } + me = (struct mailmap_entry *)map->items[index].util; + + if (old_name == NULL) { + debug_mm("mailmap: adding (simple) entry for %s at index %d\n", old_email, index); + /* Replace current name and new email for simple entry */ + free(me->name); + free(me->email); + if (new_name) + me->name = xstrdup(new_name); + if (new_email) + me->email = xstrdup(new_email); + } else { + struct mailmap_info *mi = xmalloc(sizeof(struct mailmap_info)); + debug_mm("mailmap: adding (complex) entry for %s at index %d\n", old_email, index); + if (new_name) + mi->name = xstrdup(new_name); + if (new_email) + mi->email = xstrdup(new_email); + string_list_insert(old_name, &me->namemap)->util = mi; + } + + debug_mm("mailmap: '%s' <%s> -> '%s' <%s>\n", + old_name, old_email, new_name, new_email); +} + +static char *parse_name_and_email(char *buffer, char **name, char **email) +{ + char *left, *right, *nstart, *nend; + *name = *email = 0; + + if ((left = strchr(buffer, '<')) == NULL) + return NULL; + if ((right = strchr(left+1, '>')) == NULL) + return NULL; + if (left+1 == right) + return NULL; + + /* remove whitespace from beginning and end of name */ + nstart = buffer; + while (isspace(*nstart) && nstart < left) + ++nstart; + nend = left-1; + while (isspace(*nend) && nend > nstart) + --nend; + + *name = (nstart < nend ? nstart : NULL); + *email = left+1; + *(nend+1) = '\0'; + *right++ = '\0'; + + return (*right == '\0' ? NULL : right); +} + +static int read_single_mailmap(struct string_list *map, const char *filename, char **repo_abbrev) { char buffer[1024]; - FILE *f = fopen(filename, "r"); + FILE *f = (filename == NULL ? NULL : fopen(filename, "r")); if (f == NULL) return 1; while (fgets(buffer, sizeof(buffer), f) != NULL) { - char *end_of_name, *left_bracket, *right_bracket; - char *name, *email; - int i; + char *name1 = 0, *email1 = 0, *name2 = 0, *email2 = 0; if (buffer[0] == '#') { static const char abbrev[] = "# repo-abbrev:"; int abblen = sizeof(abbrev) - 1; @@ -36,41 +150,49 @@ int read_mailmap(struct string_list *map, const char *filename, char **repo_abbr } continue; } - if ((left_bracket = strchr(buffer, '<')) == NULL) - continue; - if ((right_bracket = strchr(left_bracket + 1, '>')) == NULL) - continue; - if (right_bracket == left_bracket + 1) - continue; - for (end_of_name = left_bracket; - end_of_name != buffer && isspace(end_of_name[-1]); - end_of_name--) - ; /* keep on looking */ - if (end_of_name == buffer) - continue; - name = xmalloc(end_of_name - buffer + 1); - strlcpy(name, buffer, end_of_name - buffer + 1); - email = xmalloc(right_bracket - left_bracket); - for (i = 0; i < right_bracket - left_bracket - 1; i++) - email[i] = tolower(left_bracket[i + 1]); - email[right_bracket - left_bracket - 1] = '\0'; - string_list_insert(email, map)->util = name; + if ((name2 = parse_name_and_email(buffer, &name1, &email1)) != NULL) + parse_name_and_email(name2, &name2, &email2); + + if (email1) + add_mapping(map, name1, email1, name2, email2); } fclose(f); return 0; } -int map_email(struct string_list *map, const char *email, char *name, int maxlen) +int read_mailmap(struct string_list *map, char **repo_abbrev) +{ + map->strdup_strings = 1; + /* each failure returns 1, so >1 means both calls failed */ + return read_single_mailmap(map, ".mailmap", repo_abbrev) + + read_single_mailmap(map, git_mailmap_file, repo_abbrev) > 1; +} + +void clear_mailmap(struct string_list *map) +{ + debug_mm("mailmap: clearing %d entries...\n", map->nr); + map->strdup_strings = 1; + string_list_clear_func(map, free_mailmap_entry); + debug_mm("mailmap: cleared\n"); +} + +int map_user(struct string_list *map, + char *email, int maxlen_email, char *name, int maxlen_name) { char *p; struct string_list_item *item; + struct mailmap_entry *me; char buf[1024], *mailbuf; int i; - /* autocomplete common developers */ + /* figure out space requirement for email */ p = strchr(email, '>'); - if (!p) - return 0; + if (!p) { + /* email passed in might not be wrapped in <>, but end with a \0 */ + p = memchr(email, '\0', maxlen_email); + if (p == 0) + return 0; + } if (p - email + 1 < sizeof(buf)) mailbuf = buf; else @@ -80,13 +202,39 @@ int map_email(struct string_list *map, const char *email, char *name, int maxlen for (i = 0; i < p - email; i++) mailbuf[i] = tolower(email[i]); mailbuf[i] = 0; + + debug_mm("map_user: map '%s' <%s>\n", name, mailbuf); item = string_list_lookup(mailbuf, map); + if (item != NULL) { + me = (struct mailmap_entry *)item->util; + if (me->namemap.nr) { + /* The item has multiple items, so we'll look up on name too */ + /* If the name is not found, we choose the simple entry */ + struct string_list_item *subitem = string_list_lookup(name, &me->namemap); + if (subitem) + item = subitem; + } + } if (mailbuf != buf) free(mailbuf); if (item != NULL) { - const char *realname = (const char *)item->util; - strlcpy(name, realname, maxlen); + struct mailmap_info *mi = (struct mailmap_info *)item->util; + if (mi->name == NULL && (mi->email == NULL || maxlen_email == 0)) { + debug_mm("map_user: -- (no simple mapping)\n"); + return 0; + } + if (maxlen_email && mi->email) + strlcpy(email, mi->email, maxlen_email); + if (maxlen_name && mi->name) + strlcpy(name, mi->name, maxlen_name); + debug_mm("map_user: to '%s' <%s>\n", name, mi->email ? mi->email : ""); return 1; } + debug_mm("map_user: --\n"); return 0; } + +int map_email(struct string_list *map, const char *email, char *name, int maxlen) +{ + return map_user(map, (char *)email, 0, name, maxlen); +} @@ -1,7 +1,11 @@ #ifndef MAILMAP_H #define MAILMAP_H -int read_mailmap(struct string_list *map, const char *filename, char **repo_abbrev); +int read_mailmap(struct string_list *map, char **repo_abbrev); +void clear_mailmap(struct string_list *map); + int map_email(struct string_list *mailmap, const char *email, char *name, int maxlen); +int map_user(struct string_list *mailmap, + char *email, int maxlen_email, char *name, int maxlen_name); #endif 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 @@ -154,7 +154,7 @@ int validate_headref(const char *path) /* Make sure it is a "refs/.." symlink */ if (S_ISLNK(st.st_mode)) { len = readlink(path, buffer, sizeof(buffer)-1); - if (len >= 11 && !memcmp("refs/heads/", buffer, 11)) + if (len >= 5 && !memcmp("refs/", buffer, 5)) return 0; return -1; } @@ -178,7 +178,7 @@ int validate_headref(const char *path) len -= 4; while (len && isspace(*buf)) buf++, len--; - if (len >= 11 && !memcmp("refs/heads/", buf, 11)) + if (len >= 5 && !memcmp("refs/", buf, 5)) return 0; } @@ -499,3 +499,39 @@ int longest_ancestor_length(const char *path, const char *prefix_list) return max_len; } + +/* strip arbitrary amount of directory separators at end of path */ +static inline int chomp_trailing_dir_sep(const char *path, int len) +{ + while (len && is_dir_sep(path[len - 1])) + len--; + return len; +} + +/* + * If path ends with suffix (complete path components), returns the + * part before suffix (sans trailing directory separators). + * Otherwise returns NULL. + */ +char *strip_path_suffix(const char *path, const char *suffix) +{ + int path_len = strlen(path), suffix_len = strlen(suffix); + + while (suffix_len) { + if (!path_len) + return NULL; + + if (is_dir_sep(path[path_len - 1])) { + if (!is_dir_sep(suffix[suffix_len - 1])) + return NULL; + path_len = chomp_trailing_dir_sep(path, path_len); + suffix_len = chomp_trailing_dir_sep(suffix, suffix_len); + } + else if (path[--path_len] != suffix[--suffix_len]) + return NULL; + } + + if (path_len && !is_dir_sep(path[path_len - 1])) + return NULL; + return xstrndup(path, chomp_trailing_dir_sep(path, path_len)); +} @@ -210,15 +210,13 @@ static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb, while (parent) { struct commit *p = parent->item; const char *hex = NULL; - const char *dots; if (abbrev) hex = find_unique_abbrev(p->object.sha1, abbrev); if (!hex) hex = sha1_to_hex(p->object.sha1); - dots = (abbrev && strlen(hex) != 40) ? "..." : ""; parent = parent->next; - strbuf_addf(sb, " %s%s", hex, dots); + strbuf_addf(sb, " %s", hex); } strbuf_addch(sb, '\n'); } @@ -305,23 +303,14 @@ static char *logmsg_reencode(const struct commit *commit, return out; } -static int mailmap_name(struct strbuf *sb, const char *email) +static int mailmap_name(char *email, int email_len, char *name, int name_len) { static struct string_list *mail_map; - char buffer[1024]; - if (!mail_map) { mail_map = xcalloc(1, sizeof(*mail_map)); - read_mailmap(mail_map, ".mailmap", NULL); + read_mailmap(mail_map, NULL); } - - if (!mail_map->nr) - return -1; - - if (!map_email(mail_map, email, buffer, sizeof(buffer))) - return -1; - strbuf_addstr(sb, buffer); - return 0; + return mail_map->nr && map_user(mail_map, email, email_len, name, name_len); } static size_t format_person_part(struct strbuf *sb, char part, @@ -332,6 +321,9 @@ static size_t format_person_part(struct strbuf *sb, char part, int start, end, tz = 0; unsigned long date = 0; char *ep; + const char *name_start, *name_end, *mail_start, *mail_end, *msg_end = msg+len; + char person_name[1024]; + char person_mail[1024]; /* advance 'end' to point to email start delimiter */ for (end = 0; end < len && msg[end] != '<'; end++) @@ -345,25 +337,34 @@ static size_t format_person_part(struct strbuf *sb, char part, if (end >= len - 2) goto skip; + /* Seek for both name and email part */ + name_start = msg; + name_end = msg+end; + while (name_end > name_start && isspace(*(name_end-1))) + name_end--; + mail_start = msg+end+1; + mail_end = mail_start; + while (mail_end < msg_end && *mail_end != '>') + mail_end++; + if (mail_end == msg_end) + goto skip; + end = mail_end-msg; + + if (part == 'N' || part == 'E') { /* mailmap lookup */ + strlcpy(person_name, name_start, name_end-name_start+1); + strlcpy(person_mail, mail_start, mail_end-mail_start+1); + mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name)); + name_start = person_name; + name_end = name_start + strlen(person_name); + mail_start = person_mail; + mail_end = mail_start + strlen(person_mail); + } if (part == 'n' || part == 'N') { /* name */ - while (end > 0 && isspace(msg[end - 1])) - end--; - if (part != 'N' || !msg[end] || !msg[end + 1] || - mailmap_name(sb, msg + end + 2) < 0) - strbuf_add(sb, msg, end); + strbuf_add(sb, name_start, name_end-name_start); return placeholder_len; } - start = ++end; /* save email start position */ - - /* advance 'end' to point to email end delimiter */ - for ( ; end < len && msg[end] != '>'; end++) - ; /* do nothing */ - - if (end >= len) - goto skip; - - if (part == 'e') { /* email */ - strbuf_add(sb, msg + start, end - start); + if (part == 'e' || part == 'E') { /* email */ + strbuf_add(sb, mail_start, mail_end-mail_start); return placeholder_len; } @@ -567,16 +568,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; 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/string-list.c b/string-list.c index ddd83c8c76..15e14cf47a 100644 --- a/string-list.c +++ b/string-list.c @@ -26,10 +26,10 @@ static int get_entry_index(const struct string_list *list, const char *string, } /* returns -1-index if already exists */ -static int add_entry(struct string_list *list, const char *string) +static int add_entry(int insert_at, struct string_list *list, const char *string) { - int exact_match; - int index = get_entry_index(list, string, &exact_match); + int exact_match = 0; + int index = insert_at != -1 ? insert_at : get_entry_index(list, string, &exact_match); if (exact_match) return -1 - index; @@ -53,7 +53,13 @@ static int add_entry(struct string_list *list, const char *string) struct string_list_item *string_list_insert(const char *string, struct string_list *list) { - int index = add_entry(list, string); + return string_list_insert_at_index(-1, string, list); +} + +struct string_list_item *string_list_insert_at_index(int insert_at, + const char *string, struct string_list *list) +{ + int index = add_entry(insert_at, list, string); if (index < 0) index = -1 - index; @@ -68,6 +74,16 @@ int string_list_has_string(const struct string_list *list, const char *string) return exact_match; } +int string_list_find_insert_index(const struct string_list *list, const char *string, + int negative_existing_index) +{ + int exact_match; + int index = get_entry_index(list, string, &exact_match); + if (exact_match) + index = -1 - (negative_existing_index ? index : 0); + return index; +} + struct string_list_item *string_list_lookup(const char *string, struct string_list *list) { int exact_match, i = get_entry_index(list, string, &exact_match); @@ -94,6 +110,25 @@ void string_list_clear(struct string_list *list, int free_util) list->nr = list->alloc = 0; } +void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc) +{ + if (list->items) { + int i; + if (clearfunc) { + for (i = 0; i < list->nr; i++) + clearfunc(list->items[i].util, list->items[i].string); + } + if (list->strdup_strings) { + for (i = 0; i < list->nr; i++) + free(list->items[i].string); + } + free(list->items); + } + list->items = NULL; + list->nr = list->alloc = 0; +} + + void print_string_list(const char *text, const struct string_list *p) { int i; diff --git a/string-list.h b/string-list.h index 4d6a7051fe..d32ba05202 100644 --- a/string-list.h +++ b/string-list.h @@ -15,9 +15,18 @@ struct string_list void print_string_list(const char *text, const struct string_list *p); void string_list_clear(struct string_list *list, int free_util); +/* Use this function to call a custom clear function on each util pointer */ +/* The string associated with the util pointer is passed as the second argument */ +typedef void (*string_list_clear_func_t)(void *p, const char *str); +void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc); + /* Use these functions only on sorted lists: */ int string_list_has_string(const struct string_list *list, const char *string); +int string_list_find_insert_index(const struct string_list *list, const char *string, + int negative_existing_index); struct string_list_item *string_list_insert(const char *string, struct string_list *list); +struct string_list_item *string_list_insert_at_index(int insert_at, + const char *string, struct string_list *list); struct string_list_item *string_list_lookup(const char *string, struct string_list *list); /* Use these functions only on unsorted lists: */ 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..af6e5e1d6a 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -5,6 +5,12 @@ LogFormat "%h %l %u %t \"%r\" %>s %b" common CustomLog access.log common ErrorLog error.log +<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/t0060-path-utils.sh b/t/t0060-path-utils.sh index 4ed1f0b4dd..8336114f98 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -85,4 +85,8 @@ ancestor /foo/bar :://foo/.:: 4 ancestor /foo/bar //foo/./::/bar 4 ancestor /foo/bar ::/bar -1 +test_expect_success 'strip_path_suffix' ' + test c:/msysgit = $(test-path-utils strip_path_suffix \ + c:/msysgit/libexec//git-core libexec/git-core) +' test_done diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh new file mode 100755 index 0000000000..315b9b3f10 --- /dev/null +++ b/t/t0100-previous.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +test_description='previous branch syntax @{-n}' + +. ./test-lib.sh + +test_expect_success 'branch -d @{-1}' ' + test_commit A && + git checkout -b junk && + git checkout - && + test "$(git symbolic-ref HEAD)" = refs/heads/master && + git branch -d @{-1} && + test_must_fail git rev-parse --verify refs/heads/junk +' + +test_expect_success 'branch -d @{-12} when there is not enough switches yet' ' + git reflog expire --expire=now && + git checkout -b junk2 && + git checkout - && + test "$(git symbolic-ref HEAD)" = refs/heads/master && + test_must_fail git branch -d @{-12} && + git rev-parse --verify refs/heads/master +' + +test_expect_success 'merge @{-1}' ' + git checkout A && + test_commit B && + git checkout A && + test_commit C && + git branch -f master B && + git branch -f other && + git checkout other && + git checkout master && + git merge @{-1} && + git cat-file commit HEAD | grep "Merge branch '\''other'\''" +' + +test_expect_success 'merge @{-1} when there is not enough switches yet' ' + git reflog expire --expire=now && + git checkout -f master && + git reset --hard B && + git branch -f other C && + git checkout other && + git checkout master && + test_must_fail git merge @{-12} +' + +test_done + diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh index 569f34177d..7fa5f5b22a 100755 --- a/t/t1401-symbolic-ref.sh +++ b/t/t1401-symbolic-ref.sh @@ -27,11 +27,6 @@ test_expect_success 'symbolic-ref refuses non-ref for HEAD' ' ' reset_to_sane -test_expect_success 'symbolic-ref refuses non-branch for HEAD' ' - test_must_fail git symbolic-ref HEAD refs/foo -' -reset_to_sane - test_expect_success 'symbolic-ref refuses bare sha1' ' echo content >file && git add file && git commit -m one test_must_fail git symbolic-ref HEAD `git rev-parse HEAD` diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh index 85da4caa7e..48ee07779d 100755 --- a/t/t1500-rev-parse.sh +++ b/t/t1500-rev-parse.sh @@ -26,21 +26,28 @@ test_rev_parse() { "test '$1' = \"\$(git rev-parse --show-prefix)\"" shift [ $# -eq 0 ] && return + + test_expect_success "$name: git-dir" \ + "test '$1' = \"\$(git rev-parse --git-dir)\"" + shift + [ $# -eq 0 ] && return } -# label is-bare is-inside-git is-inside-work prefix +# label is-bare is-inside-git is-inside-work prefix git-dir + +ROOT=$(pwd) -test_rev_parse toplevel false false true '' +test_rev_parse toplevel false false true '' .git cd .git || exit 1 -test_rev_parse .git/ false true false '' +test_rev_parse .git/ false true false '' . cd objects || exit 1 -test_rev_parse .git/objects/ false true false '' +test_rev_parse .git/objects/ false true false '' "$ROOT/.git" cd ../.. || exit 1 mkdir -p sub/dir || exit 1 cd sub/dir || exit 1 -test_rev_parse subdirectory false false true sub/dir/ +test_rev_parse subdirectory false false true sub/dir/ "$ROOT/.git" cd ../.. || exit 1 git config core.bare true diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh index 27dc6c55d5..f6a6f839a1 100755 --- a/t/t1501-worktree.sh +++ b/t/t1501-worktree.sh @@ -92,13 +92,6 @@ cd sub/dir || exit 1 test_rev_parse 'in repo.git/sub/dir' false true true sub/dir/ cd ../../../.. || exit 1 -test_expect_success 'detecting gitdir when cwd is in a subdir of gitdir' ' - (expected=$(pwd)/repo.git && - cd repo.git/refs && - unset GIT_DIR && - test "$expected" = "$(git rev-parse --git-dir)") -' - test_expect_success 'repo finds its work tree' ' (cd repo.git && : > work/sub/dir/untracked && diff --git a/t/t2300-cd-to-toplevel.sh b/t/t2300-cd-to-toplevel.sh index e42cbfe6c6..293dc353b1 100755 --- a/t/t2300-cd-to-toplevel.sh +++ b/t/t2300-cd-to-toplevel.sh @@ -10,12 +10,12 @@ test_cd_to_toplevel () { cd '"'$1'"' && . git-sh-setup && cd_to_toplevel && - [ "$(unset PWD; /bin/pwd)" = "$TOPLEVEL" ] + [ "$(pwd -P)" = "$TOPLEVEL" ] ) ' } -TOPLEVEL="$(unset PWD; /bin/pwd)/repo" +TOPLEVEL="$(pwd -P)/repo" mkdir -p repo/sub/dir mv .git repo/ SUBDIRECTORY_OK=1 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/t4013-diff-various.sh b/t/t4013-diff-various.sh index aba53202f8..9c709022ef 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -74,6 +74,10 @@ test_expect_success setup ' for i in 1 2; do echo $i; done >>dir/sub && git update-index file0 dir/sub && + mkdir dir3 && + cp dir/sub dir3/sub && + test-chmtime +1 dir3/sub && + git config log.showroot false && git commit --amend && git show-branch @@ -262,6 +266,7 @@ diff --patch-with-raw -r initial..side diff --name-status dir2 dir diff --no-index --name-status dir2 dir diff --no-index --name-status -- dir2 dir +diff --no-index dir dir3 diff master master^ side EOF diff --git a/t/t4013/diff.diff_--no-index_dir_dir3 b/t/t4013/diff.diff_--no-index_dir_dir3 new file mode 100644 index 0000000000..2142c2b9ad --- /dev/null +++ b/t/t4013/diff.diff_--no-index_dir_dir3 @@ -0,0 +1,2 @@ +$ git diff --no-index dir dir3 +$ diff --git a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ index 3ceb8e73c5..bd7f5c0f70 100644 --- a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ +++ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ @@ -1,6 +1,6 @@ $ git log --patch-with-stat --summary master -- dir/ commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--patch-with-stat_master b/t/t4013/diff.log_--patch-with-stat_master index 43d77761f9..14595a614c 100644 --- a/t/t4013/diff.log_--patch-with-stat_master +++ b/t/t4013/diff.log_--patch-with-stat_master @@ -1,6 +1,6 @@ $ git log --patch-with-stat master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_ index 5187a26816..5a4e72765d 100644 --- a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ +++ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_ @@ -1,6 +1,6 @@ $ git log --patch-with-stat master -- dir/ commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master index c9640976a8..df0aaa9f2c 100644 --- a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master +++ b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master @@ -1,6 +1,6 @@ $ git log --root --cc --patch-with-stat --summary master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master index ad050af55f..c11b5f2c7f 100644 --- a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master +++ b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master @@ -1,6 +1,6 @@ $ git log --root --patch-with-stat --summary master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--root_--patch-with-stat_master b/t/t4013/diff.log_--root_--patch-with-stat_master index 628c6c03bc..5f0c98f9ce 100644 --- a/t/t4013/diff.log_--root_--patch-with-stat_master +++ b/t/t4013/diff.log_--root_--patch-with-stat_master @@ -1,6 +1,6 @@ $ git log --root --patch-with-stat master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master index 5d4e0f13b5..e62c368dc6 100644 --- a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master +++ b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master @@ -1,6 +1,6 @@ $ git log --root -c --patch-with-stat --summary master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--root_-p_master b/t/t4013/diff.log_--root_-p_master index 217a2eb203..b42c334439 100644 --- a/t/t4013/diff.log_--root_-p_master +++ b/t/t4013/diff.log_--root_-p_master @@ -1,6 +1,6 @@ $ git log --root -p master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--root_master b/t/t4013/diff.log_--root_master index e17ccfc234..e8f46159da 100644 --- a/t/t4013/diff.log_--root_master +++ b/t/t4013/diff.log_--root_master @@ -1,6 +1,6 @@ $ git log --root master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_-p_master b/t/t4013/diff.log_-p_master index f8fefef2c3..bf1326dc36 100644 --- a/t/t4013/diff.log_-p_master +++ b/t/t4013/diff.log_-p_master @@ -1,6 +1,6 @@ $ git log -p master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_master b/t/t4013/diff.log_master index e9d9e7b40a..a8f6ce5abd 100644 --- a/t/t4013/diff.log_master +++ b/t/t4013/diff.log_master @@ -1,6 +1,6 @@ $ git log master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.show_master b/t/t4013/diff.show_master index 9e6e1f2710..fb08ce0e46 100644 --- a/t/t4013/diff.show_master +++ b/t/t4013/diff.show_master @@ -1,6 +1,6 @@ $ git show master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master index 5facf2543d..e96ff1fb8c 100644 --- a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master +++ b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master @@ -1,6 +1,6 @@ $ git whatchanged --root --cc --patch-with-stat --summary master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master index 10f6767e49..c0aff68ef6 100644 --- a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master +++ b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master @@ -1,6 +1,6 @@ $ git whatchanged --root -c --patch-with-stat --summary master commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 -Merge: 9a6d494... c7a2ab9... +Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index caea292f15..281680d95a 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -128,4 +128,12 @@ test_expect_success 'force diff with "diff"' ' test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual ' +test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' ' + echo anotherfile > file2 && + git add file2 && + git commit -m "added 2nd file" && + echo modified >file2 && + GIT_EXTERNAL_DIFF=echo git diff +' + test_done diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh new file mode 100755 index 0000000000..9a7d1b4466 --- /dev/null +++ b/t/t4203-mailmap.sh @@ -0,0 +1,215 @@ +#!/bin/sh + +test_description='.mailmap configurations' + +. ./test-lib.sh + +test_expect_success setup ' + echo one >one && + git add one && + test_tick && + git commit -m initial && + echo two >>one && + git add one && + git commit --author "nick1 <bugs@company.xx>" -m second +' + +cat >expect <<\EOF +A U Thor (1): + initial + +nick1 (1): + second + +EOF + +test_expect_success 'No mailmap' ' + git shortlog HEAD >actual && + test_cmp expect actual +' + +cat >expect <<\EOF +Repo Guy (1): + initial + +nick1 (1): + second + +EOF + +test_expect_success 'default .mailmap' ' + echo "Repo Guy <author@example.com>" > .mailmap && + git shortlog HEAD >actual && + test_cmp expect actual +' + +# Using a mailmap file in a subdirectory of the repo here, but +# could just as well have been a file outside of the repository +cat >expect <<\EOF +Internal Guy (1): + second + +Repo Guy (1): + initial + +EOF +test_expect_success 'mailmap.file set' ' + mkdir internal_mailmap && + echo "Internal Guy <bugs@company.xx>" > internal_mailmap/.mailmap && + git config mailmap.file internal_mailmap/.mailmap && + git shortlog HEAD >actual && + test_cmp expect actual +' + +cat >expect <<\EOF +External Guy (1): + initial + +Internal Guy (1): + second + +EOF +test_expect_success 'mailmap.file override' ' + echo "External Guy <author@example.com>" >> internal_mailmap/.mailmap && + git config mailmap.file internal_mailmap/.mailmap && + git shortlog HEAD >actual && + test_cmp expect actual +' + +cat >expect <<\EOF +Repo Guy (1): + initial + +nick1 (1): + second + +EOF + +test_expect_success 'mailmap.file non-existant' ' + rm internal_mailmap/.mailmap && + rmdir internal_mailmap && + git shortlog HEAD >actual && + test_cmp expect actual +' + +cat >expect <<\EOF +A U Thor (1): + initial + +nick1 (1): + second + +EOF +test_expect_success 'No mailmap files, but configured' ' + rm .mailmap && + git shortlog HEAD >actual && + test_cmp expect actual +' + +# Extended mailmap configurations should give us the following output for shortlog +cat >expect <<\EOF +A U Thor <author@example.com> (1): + initial + +CTO <cto@company.xx> (1): + seventh + +Other Author <other@author.xx> (2): + third + fourth + +Santa Claus <santa.claus@northpole.xx> (2): + fifth + sixth + +Some Dude <some@dude.xx> (1): + second + +EOF + +test_expect_success 'Shortlog output (complex mapping)' ' + echo three >>one && + git add one && + test_tick && + git commit --author "nick2 <bugs@company.xx>" -m third && + + echo four >>one && + git add one && + test_tick && + git commit --author "nick2 <nick2@company.xx>" -m fourth && + + echo five >>one && + git add one && + test_tick && + git commit --author "santa <me@company.xx>" -m fifth && + + echo six >>one && + git add one && + test_tick && + git commit --author "claus <me@company.xx>" -m sixth && + + echo seven >>one && + git add one && + test_tick && + git commit --author "CTO <cto@coompany.xx>" -m seventh && + + mkdir internal_mailmap && + echo "Committed <committer@example.com>" > internal_mailmap/.mailmap && + echo "<cto@company.xx> <cto@coompany.xx>" >> internal_mailmap/.mailmap && + echo "Some Dude <some@dude.xx> nick1 <bugs@company.xx>" >> internal_mailmap/.mailmap && + echo "Other Author <other@author.xx> nick2 <bugs@company.xx>" >> internal_mailmap/.mailmap && + echo "Other Author <other@author.xx> <nick2@company.xx>" >> internal_mailmap/.mailmap && + echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap && + echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap && + + git shortlog -e HEAD >actual && + test_cmp expect actual + +' + +# git log with --pretty format which uses the name and email mailmap placemarkers +cat >expect <<\EOF +Author CTO <cto@coompany.xx> maps to CTO <cto@company.xx> +Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com> + +Author claus <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx> +Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com> + +Author santa <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx> +Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com> + +Author nick2 <nick2@company.xx> maps to Other Author <other@author.xx> +Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com> + +Author nick2 <bugs@company.xx> maps to Other Author <other@author.xx> +Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com> + +Author nick1 <bugs@company.xx> maps to Some Dude <some@dude.xx> +Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com> + +Author A U Thor <author@example.com> maps to A U Thor <author@example.com> +Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com> +EOF + +test_expect_success 'Log output (complex mapping)' ' + git log --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual && + test_cmp expect actual +' + +# git blame +cat >expect <<\EOF +^3a2fdcb (A U Thor 2005-04-07 15:13:13 -0700 1) one +7de6f99b (Some Dude 2005-04-07 15:13:13 -0700 2) two +5815879d (Other Author 2005-04-07 15:14:13 -0700 3) three +ff859d96 (Other Author 2005-04-07 15:15:13 -0700 4) four +5ab6d4fa (Santa Claus 2005-04-07 15:16:13 -0700 5) five +38a42d8b (Santa Claus 2005-04-07 15:17:13 -0700 6) six +8ddc0386 (CTO 2005-04-07 15:18:13 -0700 7) seven +EOF + +test_expect_success 'Blame output (complex mapping)' ' + git blame one >actual && + test_cmp expect actual +' + +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/t5304-prune.sh b/t/t5304-prune.sh index 771c0a06a4..55ed7c7935 100755 --- a/t/t5304-prune.sh +++ b/t/t5304-prune.sh @@ -112,4 +112,42 @@ test_expect_success 'prune: do not prune heads listed as an argument' ' ' +test_expect_success 'gc --no-prune' ' + + before=$(git count-objects | sed "s/ .*//") && + BLOB=$(echo aleph_0 | git hash-object -w --stdin) && + BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") && + test $((1 + $before)) = $(git count-objects | sed "s/ .*//") && + test -f $BLOB_FILE && + test-chmtime =-$((86400*5001)) $BLOB_FILE && + git config gc.pruneExpire 2.days.ago && + git gc --no-prune && + test 1 = $(git count-objects | sed "s/ .*//") && + test -f $BLOB_FILE + +' + +test_expect_success 'gc respects gc.pruneExpire' ' + + git config gc.pruneExpire 5002.days.ago && + git gc && + test -f $BLOB_FILE && + git config gc.pruneExpire 5000.days.ago && + git gc && + test ! -f $BLOB_FILE + +' + +test_expect_success 'gc --prune=<date>' ' + + BLOB=$(echo aleph_0 | git hash-object -w --stdin) && + BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") && + test-chmtime =-$((86400*5001)) $BLOB_FILE && + git gc --prune=5002.days.ago && + test -f $BLOB_FILE && + git gc --prune=5000.days.ago && + test ! -f $BLOB_FILE + +' + test_done diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index c236b5e83b..10e5fd0d5a 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -94,6 +94,18 @@ test_expect_success 'MKCOL sends directory names with trailing slashes' ' ' +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" + +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] " + +' + stop_httpd test_done diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index fe287d31fb..44793f2eee 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -144,4 +144,19 @@ test_expect_success 'clone to an existing path' ' test_must_fail git clone src target-5 ' +test_expect_success 'clone a void' ' + mkdir src-0 && + ( + cd src-0 && git init + ) && + git clone src-0 target-6 && + ( + cd src-0 && test_commit A + ) && + git clone src-0 target-7 && + # There is no reason to insist they are bit-for-bit + # identical, but this test should suffice for now. + test_cmp target-6/.git/config target-7/.git/config +' + 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/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index 56b5eccdb4..329c851685 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -48,6 +48,18 @@ test_expect_success 'result is really identical' ' test $H = $(git rev-parse HEAD) ' +TRASHDIR=$(pwd) +test_expect_success 'correct GIT_DIR while using -d' ' + mkdir drepo && + ( cd drepo && + git init && + test_commit drepo && + git filter-branch -d "$TRASHDIR/dfoo" \ + --index-filter "cp \"$TRASHDIR\"/dfoo/backup-refs \"$TRASHDIR\"" \ + ) && + grep drepo "$TRASHDIR/backup-refs" +' + test_expect_success 'Fail if commit filter fails' ' test_must_fail git filter-branch -f --commit-filter "exit 1" HEAD ' 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/t9301-fast-export.sh b/t/t9301-fast-export.sh index 9985721055..86c376088c 100755 --- a/t/t9301-fast-export.sh +++ b/t/t9301-fast-export.sh @@ -185,8 +185,8 @@ test_expect_success 'submodule fast-export | fast-import' ' ' -export GIT_AUTHOR_NAME='A U Thor' -export GIT_COMMITTER_NAME='C O Mitter' +GIT_AUTHOR_NAME='A U Thor'; export GIT_AUTHOR_NAME +GIT_COMMITTER_NAME='C O Mitter'; export GIT_COMMITTER_NAME test_expect_success 'setup copies' ' diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 43cd6eecba..6ed10d0933 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -43,9 +43,11 @@ gitweb_run () { GATEWAY_INTERFACE="CGI/1.1" HTTP_ACCEPT="*/*" REQUEST_METHOD="GET" + SCRIPT_NAME="$TEST_DIRECTORY/../gitweb/gitweb.perl" QUERY_STRING=""$1"" PATH_INFO=""$2"" - export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD QUERY_STRING PATH_INFO + export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \ + SCRIPT_NAME QUERY_STRING PATH_INFO GITWEB_CONFIG=$(pwd)/gitweb_config.perl export GITWEB_CONFIG @@ -54,7 +56,7 @@ gitweb_run () { # written to web server logs, so we are not interested in that: # we are interested only in properly formatted errors/warnings rm -f gitweb.log && - perl -- "$TEST_DIRECTORY/../gitweb/gitweb.perl" \ + perl -- "$SCRIPT_NAME" \ >/dev/null 2>gitweb.log && if grep "^[[]" gitweb.log >/dev/null 2>&1; then false; else true; fi @@ -660,6 +662,11 @@ cat >>gitweb_config.perl <<EOF EOF test_expect_success \ + 'config override: tree view, features not overridden in repo config' \ + 'gitweb_run "p=.git;a=tree"' +test_debug 'cat gitweb.log' + +test_expect_success \ 'config override: tree view, features disabled in repo config' \ 'git config gitweb.blame no && git config gitweb.snapshot none && @@ -667,12 +674,23 @@ test_expect_success \ test_debug 'cat gitweb.log' test_expect_success \ - 'config override: tree view, features enabled in repo config' \ + 'config override: tree view, features enabled in repo config (1)' \ 'git config gitweb.blame yes && git config gitweb.snapshot "zip,tgz, tbz2" && gitweb_run "p=.git;a=tree"' test_debug 'cat gitweb.log' +cat >.git/config <<\EOF +# testing noval and alternate separator +[gitweb] + blame + snapshot = zip tgz +EOF +test_expect_success \ + 'config override: tree view, features enabled in repo config (2)' \ + 'gitweb_run "p=.git;a=tree"' +test_debug 'cat gitweb.log' + # ---------------------------------------------------------------------- # non-ASCII in README.html diff --git a/t/test-lib.sh b/t/test-lib.sh index 6f6244ab7e..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 @@ -82,7 +98,7 @@ do -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate) immediate=t; shift ;; -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests) - export GIT_TEST_LONG=t; shift ;; + GIT_TEST_LONG=t; export GIT_TEST_LONG; shift ;; -h|--h|--he|--hel|--help) help=t; shift ;; -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) @@ -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. diff --git a/test-path-utils.c b/test-path-utils.c index 5168a8e3df..d261398d6c 100644 --- a/test-path-utils.c +++ b/test-path-utils.c @@ -26,6 +26,12 @@ int main(int argc, char **argv) return 0; } + if (argc == 4 && !strcmp(argv[1], "strip_path_suffix")) { + char *prefix = strip_path_suffix(argv[2], argv[3]); + printf("%s\n", prefix ? prefix : "(null)"); + return 0; + } + fprintf(stderr, "%s: unknown function name: %s\n", argv[0], argv[1] ? argv[1] : "(there was none)"); return 1; @@ -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..e15ebdc287 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -645,7 +645,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; |