diff options
50 files changed, 954 insertions, 181 deletions
diff --git a/Documentation/RelNotes-1.5.3.3.txt b/Documentation/RelNotes-1.5.3.3.txt new file mode 100644 index 0000000000..2a7bfdd5cc --- /dev/null +++ b/Documentation/RelNotes-1.5.3.3.txt @@ -0,0 +1,31 @@ +GIT v1.5.3.3 Release Notes +========================== + +Fixes since v1.5.3.2 +-------------------- + + * git-quiltimport did not like it when a patch described in the + series file does not exist. + + * p4 importer missed executable bit in some cases. + + * The default shell on some FreeBSD did not execute the + argument parsing code correctly and made git unusable. + + * git-svn incorrectly spawned pager even when the user user + explicitly asked not to. + + * sample post-receive hook overquoted the envelope sender + value. + + * git-am got confused when the patch contained a change that is + only about type and not contents. + + * git-mergetool did not show our and their version of the + conflicted file when started from a subdirectory of the + project. + + * git-mergetool did not pass correct options when invoking diff3. + + * git-log sometimes invoked underlying "diff" machinery + unnecessarily. diff --git a/Documentation/RelNotes-1.5.3.4.txt b/Documentation/RelNotes-1.5.3.4.txt new file mode 100644 index 0000000000..b04b3a45a5 --- /dev/null +++ b/Documentation/RelNotes-1.5.3.4.txt @@ -0,0 +1,35 @@ +GIT v1.5.3.4 Release Notes +========================== + +Fixes since v1.5.3.3 +-------------------- + + * Change to "git-ls-files" in v1.5.3.3 that was introduced to support + partial commit of removal better had a segfaulting bug, which was + diagnosed and fixed by Keith and Carl. + + * Performance improvements for rename detection has been backported + from the 'master' branch. + + * "git-for-each-ref --format='%(numparent)'" was not working + correctly at all, and --format='%(parent)' was not working for + merge commits. + + * Sample "post-receive-hook" incorrectly sent out push + notification e-mails marked as "From: " the committer of the + commit that happened to be at the tip of the branch that was + pushed, not from the person who pushed. + + * "git-remote" did not exit non-zero status upon error. + + * "git-add -i" did not respond very well to EOF from tty nor + bogus input. + + * "git-rebase -i" squash subcommand incorrectly made the + author of later commit the author of resulting commit, + instead of taking from the first one in the squashed series. + + * "git-stash apply --index" was not documented. + + * autoconfiguration learned that "ar" command is found as "gas" on + some systems. diff --git a/Documentation/config.txt b/Documentation/config.txt index 2f04226988..eebb0b6ba2 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -592,7 +592,7 @@ merge.summary:: merge.tool:: Controls which merge resolution program is used by - gitlink:git-mergetool[l]. Valid values are: "kdiff3", "tkdiff", + gitlink:git-mergetool[1]. Valid values are: "kdiff3", "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and "opendiff". merge.verbosity:: diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt index 4b4fd9a506..6b2590d072 100644 --- a/Documentation/core-tutorial.txt +++ b/Documentation/core-tutorial.txt @@ -1459,7 +1459,8 @@ Although git is a truly distributed system, it is often convenient to organize your project with an informal hierarchy of developers. Linux kernel development is run this way. There is a nice illustration (page 17, "Merges to Mainline") in -link:http://tinyurl.com/a2jdg[Randy Dunlap's presentation]. +link:http://www.xenotime.net/linux/mentor/linux-mentoring-2006.pdf +[Randy Dunlap's presentation]. It should be stressed that this hierarchy is purely *informal*. There is nothing fundamental in git that enforces the "chain of diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 228ccaf10a..b1f528ae88 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -179,8 +179,8 @@ --ext-diff:: Allow an external diff helper to be executed. If you set an - external diff driver with gitlink:gitattributes(5), you need - to use this option with gitlink:git-log(1) and friends. + external diff driver with gitlink:gitattributes[5], you need + to use this option with gitlink:git-log[1] and friends. --no-ext-diff:: Disallow external diff drivers. diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 33bc31b0d4..b7285bcdbc 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -26,6 +26,10 @@ It will start out with a head equal to the one given as <start-point>. If no <start-point> is given, the branch will be created with a head equal to that of the currently checked out branch. +Note that this will create the new branch, but it will not switch the +working tree to it; use "git checkout <newbranch>" to switch to the +new branch. + When a local branch is started off a remote branch, git can setup the branch so that gitlink:git-pull[1] will appropriately merge from that remote branch. If this behavior is desired, it is possible to make it @@ -91,6 +95,21 @@ OPTIONS --no-abbrev:: Display the full sha1s in output listing rather than abbreviating them. +--track:: + Set up configuration so that git-pull will automatically + retrieve data from the remote branch. Use this if you always + pull from the same remote branch into the new branch, or if you + don't want to use "git pull <repository> <refspec>" explicitly. Set the + branch.autosetupmerge configuration variable to true if you + want git-checkout and git-branch to always behave as if + '--track' were given. + +--no-track:: + When -b is given and a branch is created off a remote branch, + set up configuration so that git-pull will not retrieve data + from the remote branch, ignoring the branch.autosetupmerge + configuration variable. + <branchname>:: The name of the branch to create or delete. The new branch name must pass all checks defined by diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt index 5051e2bada..0cc6511bdf 100644 --- a/Documentation/git-bundle.txt +++ b/Documentation/git-bundle.txt @@ -103,14 +103,20 @@ We set a tag in R1 (lastR2bundle) after the previous such transport, and move it afterwards to help build the bundle. in R1 on A: + +------------ $ git-bundle create mybundle master ^lastR2bundle $ git tag -f lastR2bundle master +------------ (move mybundle from A to B by some mechanism) in R2 on B: + +------------ $ git-bundle verify mybundle $ git-fetch mybundle refspec +------------ where refspec is refInBundle:localRef @@ -124,9 +130,11 @@ Also, with something like this in your config: You can first sneakernet the bundle file to ~/tmp/file.bdl and then these commands: +------------ $ git ls-remote bundle $ git fetch bundle $ git pull bundle +------------ would treat it as if it is talking with a remote side over the network. diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 734928bf96..2e58481ed6 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -50,7 +50,9 @@ OPTIONS --track:: When -b is given and a branch is created off a remote branch, set up configuration so that git-pull will automatically - retrieve data from the remote branch. Set the + retrieve data from the remote branch. Use this if you always + pull from the same remote branch into the new branch, or if you + don't want to use "git pull <repository> <refspec>" explicitly. Set the branch.autosetupmerge configuration variable to true if you want git-checkout and git-branch to always behave as if '--track' were given. diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 6df8e85004..f1f90cca62 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -100,6 +100,11 @@ In any case, a field name that refers to a field inapplicable to the object referred by the ref does not cause an error. It returns an empty string instead. +As a special case for the date-type fields, you may specify a format for +the date by adding one of `:default`, `:relative`, `:short`, `:local`, +`:iso8601` or `:rfc2822` to the end of the fieldname; e.g. +`%(taggerdate:relative)`. + EXAMPLES -------- diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 144bc16ff2..eae49c4876 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -56,7 +56,7 @@ merge.verbosity:: message if conflicts were detected. Level 1 outputs only conflicts, 2 outputs conflicts and file changes. Level 5 and above outputs debugging information. The default is level 2. - Can be overriden by 'GIT_MERGE_VERBOSITY' environment variable. + Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable. HOW MERGE WORKS diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 0858fa8a63..e8e75790fc 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -298,7 +298,7 @@ rebasing. If you want to fold two or more commits into one, replace the command "pick" with "squash" for the second and subsequent commit. If the commits had different authors, it will attribute the squashed commit to -the author of the last commit. +the author of the first commit. In both cases, or when a "pick" does not succeed (because of merge errors), the loop will stop to let you fix things, and you can continue diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index 94b9f17772..027ba11bdb 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -11,6 +11,7 @@ SYNOPSIS [verse] 'git-remote' 'git-remote' add [-t <branch>] [-m <branch>] [-f] [--mirror] <name> <url> +'git-remote' rm <name> 'git-remote' show <name> 'git-remote' prune <name> 'git-remote' update [group] @@ -50,6 +51,11 @@ In mirror mode, enabled with `--mirror`, the refs will not be stored in the 'refs/remotes/' namespace, but in 'refs/heads/'. This option only makes sense in bare repositories. +'rm':: + +Remove the remote named <name>. All remote tracking branches and +configuration settings for the remote are removed. + 'show':: Gives some information about the remote <name>. diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 1ec61affab..3727776a0b 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -91,6 +91,11 @@ The --cc option must be repeated for each user you want on the cc list. `/usr/lib/sendmail` if such program is available, or `localhost` otherwise. +--smtp-server-port:: + Specifies a port different from the default port (SMTP + servers typically listen to smtp port 25 and ssmtp port + 465). + --smtp-user, --smtp-pass:: Username and password for SMTP-AUTH. Defaults are the values of the configuration values 'sendemail.smtpuser' and diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 05f40cff6c..5723bb06f0 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -63,7 +63,7 @@ show [<stash>]:: it will accept any format known to `git-diff` (e.g., `git-stash show -p stash@\{1}` to view the second most recent stash in patch form). -apply [<stash>]:: +apply [--index] [<stash>]:: Restore the changes recorded in the stash on top of the current working tree state. When no `<stash>` is given, applies the latest @@ -71,6 +71,11 @@ apply [<stash>]:: + This operation can fail with conflicts; you need to resolve them by hand in the working tree. ++ +If the `--index` option is used, then tries to reinstate not only the working +tree's changes, but also the index's ones. However, this can fail, when you +have conflicts (which are stored in the index, where you therefore can no +longer apply the changes as they were originally). clear:: Remove all the stashed states. Note that those states will then diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 2c48936fcd..335e973a6a 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -21,6 +21,9 @@ add:: repository is cloned at the specified path, added to the changeset and registered in .gitmodules. If no path is specified, the path is deduced from the repository specification. + If the repository url begins with ./ or ../, it is stored as + given but resolved as a relative path from the main project's + url when cloning. status:: Show the status of the submodules. This will print the SHA-1 of the diff --git a/Documentation/git.txt b/Documentation/git.txt index cb59639f77..abce801e48 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -46,6 +46,7 @@ Documentation for older releases are available here: * link:v1.5.3/git.html[documentation for release 1.5.3] * release notes for + link:RelNotes-1.5.3.3.txt[1.5.3.3], link:RelNotes-1.5.3.2.txt[1.5.3.2], link:RelNotes-1.5.3.1.txt[1.5.3.1]. diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index d0e951ee6f..20cf8ff816 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -145,17 +145,6 @@ sign `$` upon checkout. Any byte sequence that begins with with `$Id$` upon check-in. -Interaction between checkin/checkout attributes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In the check-in codepath, the worktree file is first converted -with `ident` (if specified), and then with `crlf` (again, if -specified and applicable). - -In the check-out codepath, the blob content is first converted -with `crlf`, and then `ident`. - - `filter` ^^^^^^^^ @@ -175,11 +164,10 @@ but makes the filter a no-op passthru. The content filtering is done to massage the content into a shape that is more convenient for the platform, filesystem, and the user to use. The keyword here is "more convenient" and not -"turning something unusable into usable". In other words, it is -"hanging yourself because we gave you a long rope" if your -project uses filtering mechanism in such a way that it makes -your project unusable unless the checkout is done with a -specific filter in effect. +"turning something unusable into usable". In other words, the +intent is that if someone unsets the filter driver definition, +or does not have the appropriate filter program, the project +should still be usable. Interaction between checkin/checkout attributes diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index 9c83095693..e8b8581f52 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -26,7 +26,7 @@ precedence, the last matching pattern decides the outcome): * Patterns read from a `.gitignore` file in the same directory as the path, or in any parent directory, with patterns in the - higher level files (up to the root) being overriden by those in + higher level files (up to the root) being overridden by those in lower level files down to the directory containing the file. These patterns match relative to the location of the `.gitignore` file. A project normally includes such diff --git a/Documentation/hooks.txt b/Documentation/hooks.txt index 58b9547596..f110162b01 100644 --- a/Documentation/hooks.txt +++ b/Documentation/hooks.txt @@ -87,6 +87,20 @@ parameter, and is invoked after a commit is made. This hook is meant primarily for notification, and cannot affect the outcome of `git-commit`. +post-checkout +----------- + +This hook is invoked when a `git-checkout` is run after having updated the +worktree. The hook is given three parameters: the ref of the previous HEAD, +the ref of the new HEAD (which may or may not have changed), and a flag +indicating whether the checkout was a branch checkout (changing branches, +flag=1) or a file checkout (retrieving a file from the index, flag=0). +This hook cannot affect the outcome of `git-checkout`. + +This hook can be used to perform repository validity checks, auto-display +differences from the previous HEAD if different, or set working dir metadata +properties. + post-merge ----------- diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index a085ca1d39..c7fdf25e27 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -2856,8 +2856,7 @@ between two related tree objects, since it can ignore any entries with identical object names. (Note: in the presence of submodules, trees may also have commits as -entries. See gitlink:git-submodule[1] and gitlink:gitmodules.txt[1] -for partial documentation.) +entries. See <<submodules>> for documentation.) Note that the files all have mode 644 or 755: git actually only pays attention to the executable bit. @@ -3163,12 +3162,45 @@ information as long as you have the name of the tree that it described. Submodules ========== -This tutorial explains how to create and publish a repository with submodules -using the gitlink:git-submodule[1] command. - -Submodules maintain their own identity; the submodule support just stores the -submodule repository location and commit ID, so other developers who clone the -superproject can easily clone all the submodules at the same revision. +Large projects are often composed of smaller, self-contained modules. For +example, an embedded Linux distribution's source tree would include every +piece of software in the distribution with some local modifications; a movie +player might need to build against a specific, known-working version of a +decompression library; several independent programs might all share the same +build scripts. + +With centralized revision control systems this is often accomplished by +including every module in one single repository. Developers can check out +all modules or only the modules they need to work with. They can even modify +files across several modules in a single commit while moving things around +or updating APIs and translations. + +Git does not allow partial checkouts, so duplicating this approach in Git +would force developers to keep a local copy of modules they are not +interested in touching. Commits in an enormous checkout would be slower +than you'd expect as Git would have to scan every directory for changes. +If modules have a lot of local history, clones would take forever. + +On the plus side, distributed revision control systems can much better +integrate with external sources. In a centralized model, a single arbitrary +snapshot of the external project is exported from its own revision control +and then imported into the local revision control on a vendor branch. All +the history is hidden. With distributed revision control you can clone the +entire external history and much more easily follow development and re-merge +local changes. + +Git's submodule support allows a repository to contain, as a subdirectory, a +checkout of an external project. Submodules maintain their own identity; +the submodule support just stores the submodule repository location and +commit ID, so other developers who clone the containing project +("superproject") can easily clone all the submodules at the same revision. +Partial checkouts of the superproject are possible: you can tell Git to +clone none, some or all of the submodules. + +The gitlink:git-submodule[1] command is available since Git 1.5.3. Users +with Git 1.5.2 can look up the submodule commits in the repository and +manually check them out; earlier versions won't recognize the submodules at +all. To see how submodule support works, create (for example) four example repositories that can be used later as a submodule: @@ -3213,8 +3245,8 @@ The `git submodule add` command does a couple of things: - It clones the submodule under the current directory and by default checks out the master branch. -- It adds the submodule's clone path to the `.gitmodules` file and adds this - file to the index, ready to be committed. +- It adds the submodule's clone path to the gitlink:gitmodules[5] file and + adds this file to the index, ready to be committed. - It adds the submodule's current commit ID to the index, ready to be committed. @@ -4277,5 +4309,3 @@ Write a chapter on using plumbing and writing scripts. Alternates, clone -reference, etc. git unpack-objects -r for recovery - -submodules @@ -79,6 +79,9 @@ Issues of note: - "perl" and POSIX-compliant shells are needed to use most of the barebone Porcelainish scripts. + - "cpio" is used by git-merge for saving and restoring the index, + and by git-clone when doing a local (possibly hardlinked) clone. + - Some platform specific issues are dealt with Makefile rules, but depending on your specific installation, you may not have all the libraries/tools needed, or you may have diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index 0afa1c5c41..5dbf3e59f2 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -43,7 +43,7 @@ static struct { { "objectsize", FIELD_ULONG }, { "objectname" }, { "tree" }, - { "parent" }, /* NEEDSWORK: how to address 2nd and later parents? */ + { "parent" }, { "numparent", FIELD_ULONG }, { "object" }, { "type" }, @@ -106,7 +106,16 @@ static int parse_atom(const char *atom, const char *ep) /* Is the atom a valid one? */ for (i = 0; i < ARRAY_SIZE(valid_atom); i++) { int len = strlen(valid_atom[i].name); - if (len == ep - sp && !memcmp(valid_atom[i].name, sp, len)) + /* + * If the atom name has a colon, strip it and everything after + * it off - it specifies the format for this entry, and + * shouldn't be used for checking against the valid_atom + * table. + */ + const char *formatp = strchr(sp, ':'); + if (!formatp || ep < formatp) + formatp = ep; + if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len)) break; } @@ -262,24 +271,26 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object } if (!strcmp(name, "numparent")) { char *s = xmalloc(40); + v->ul = num_parents(commit); sprintf(s, "%lu", v->ul); v->s = s; - v->ul = num_parents(commit); } else if (!strcmp(name, "parent")) { int num = num_parents(commit); int i; struct commit_list *parents; - char *s = xmalloc(42 * num); + char *s = xmalloc(41 * num + 1); v->s = s; for (i = 0, parents = commit->parents; parents; - parents = parents->next, i = i + 42) { + parents = parents->next, i = i + 41) { struct commit *parent = parents->item; strcpy(s+i, sha1_to_hex(parent->object.sha1)); if (parents->next) s[i+40] = ' '; } + if (!i) + *s = '\0'; } } } @@ -347,12 +358,26 @@ static const char *copy_email(const char *buf) return line; } -static void grab_date(const char *buf, struct atom_value *v) +static void grab_date(const char *buf, struct atom_value *v, const char *atomname) { const char *eoemail = strstr(buf, "> "); char *zone; unsigned long timestamp; long tz; + enum date_mode date_mode = DATE_NORMAL; + const char *formatp; + + /* + * We got here because atomname ends in "date" or "date<something>"; + * it's not possible that <something> is not ":<format>" because + * parse_atom() wouldn't have allowed it, so we can assume that no + * ":" means no format is specified, and use the default. + */ + formatp = strchr(atomname, ':'); + if (formatp != NULL) { + formatp++; + date_mode = parse_date_format(formatp); + } if (!eoemail) goto bad; @@ -362,7 +387,7 @@ static void grab_date(const char *buf, struct atom_value *v) tz = strtol(zone, NULL, 10); if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE) goto bad; - v->s = xstrdup(show_date(timestamp, tz, 0)); + v->s = xstrdup(show_date(timestamp, tz, date_mode)); v->ul = timestamp; return; bad: @@ -389,7 +414,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru if (name[wholen] != 0 && strcmp(name + wholen, "name") && strcmp(name + wholen, "email") && - strcmp(name + wholen, "date")) + prefixcmp(name + wholen, "date")) continue; if (!wholine) wholine = find_wholine(who, wholen, buf, sz); @@ -401,8 +426,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru v->s = copy_name(wholine); else if (!strcmp(name + wholen, "email")) v->s = copy_email(wholine); - else if (!strcmp(name + wholen, "date")) - grab_date(wholine, v); + else if (!prefixcmp(name + wholen, "date")) + grab_date(wholine, v, name); } /* For a tag or a commit object, if "creator" or "creatordate" is @@ -422,8 +447,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru if (deref) name++; - if (!strcmp(name, "creatordate")) - grab_date(wholine, v); + if (!prefixcmp(name, "creatordate")) + grab_date(wholine, v, name); else if (!strcmp(name, "creator")) v->s = copy_line(wholine); } diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 6c1db86e80..171d449048 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -280,7 +280,8 @@ static void prune_cache(const char *prefix) if (pos < 0) pos = -pos-1; - active_cache += pos; + memmove(active_cache, active_cache + pos, + (active_nr - pos) * sizeof(struct cache_entry *)); active_nr -= pos; first = 0; last = active_nr; @@ -432,6 +432,7 @@ const char *show_date(unsigned long time, int timezone, enum date_mode mode); int parse_date(const char *date, char *buf, int bufsize); void datestamp(char *buf, int bufsize); unsigned long approxidate(const char *); +enum date_mode parse_date_format(const char *format); extern const char *git_author_info(int); extern const char *git_committer_info(int); diff --git a/configure.ac b/configure.ac index 84fd7f1e1f..ed7cc895d2 100644 --- a/configure.ac +++ b/configure.ac @@ -104,7 +104,7 @@ AC_MSG_NOTICE([CHECKS for programs]) # AC_PROG_CC([cc gcc]) #AC_PROG_INSTALL # needs install-sh or install.sh in sources -AC_CHECK_TOOL(AR, ar, :) +AC_CHECK_TOOLS(AR, [gar ar], :) AC_CHECK_PROGS(TAR, [gtar tar]) # TCLTK_PATH will be set to some value if we want Tcl/Tk # or will be empty otherwise. diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 2d77fd47ec..4286d160a0 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -36,7 +36,6 @@ ;; TODO ;; - portability to XEmacs ;; - better handling of subprocess errors -;; - hook into file save (after-save-hook) ;; - diff against other branch ;; - renaming files from the status buffer ;; - creating tags @@ -220,22 +219,15 @@ and returns the process output as a string." (message "Running git %s...done" (car args)) buffer)) -(defun git-run-command (buffer env &rest args) - (message "Running git %s..." (car args)) - (apply #'git-call-process-env buffer env args) - (message "Running git %s...done" (car args))) - (defun git-run-command-region (buffer start end env &rest args) "Run a git command with specified buffer region as input." - (message "Running git %s..." (car args)) (unless (eq 0 (if env (git-run-process-region buffer start end "env" (append (git-get-env-strings env) (list "git") args)) (git-run-process-region buffer start end "git" args))) - (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))) - (message "Running git %s...done" (car args))) + (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))) (defun git-run-hook (hook env &rest args) "Run a git hook and display its output if any." @@ -312,6 +304,13 @@ and returns the process output as a string." "\"") name)) +(defun git-success-message (text files) + "Print a success message after having handled FILES." + (let ((n (length files))) + (if (equal n 1) + (message "%s %s" text (car files)) + (message "%s %d files" text n)))) + (defun git-get-top-dir (dir) "Retrieve the top-level directory of a git tree." (let ((cdup (with-output-to-string @@ -338,7 +337,7 @@ and returns the process output as a string." (sort-lines nil (point-min) (point-max)) (save-buffer)) (when created - (git-run-command nil nil "update-index" "--add" "--" (file-relative-name ignore-name))) + (git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name))) (git-update-status-files (list (file-relative-name ignore-name)) 'unknown))) ; propertize definition for XEmacs, stolen from erc-compat @@ -485,33 +484,34 @@ and returns the process output as a string." "Remove everything from the status list." (ewoc-filter status (lambda (info) nil))) -(defun git-set-files-state (files state) - "Set the state of a list of files." - (dolist (info files) - (unless (eq (git-fileinfo->state info) state) - (setf (git-fileinfo->state info) state) - (setf (git-fileinfo->rename-state info) nil) - (setf (git-fileinfo->orig-name info) nil) - (setf (git-fileinfo->needs-refresh info) t)))) - -(defun git-set-filenames-state (status files state) - "Set the state of a list of named files." +(defun git-set-fileinfo-state (info state) + "Set the state of a file info." + (unless (eq (git-fileinfo->state info) state) + (setf (git-fileinfo->state info) state + (git-fileinfo->old-perm info) 0 + (git-fileinfo->new-perm info) 0 + (git-fileinfo->rename-state info) nil + (git-fileinfo->orig-name info) nil + (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." (when files (setq files (sort files #'string-lessp)) (let ((file (pop files)) (node (ewoc-nth status 0))) (while (and file node) (let ((info (ewoc-data node))) - (cond ((string-lessp (git-fileinfo->name info) file) - (setq node (ewoc-next status node))) - ((string-equal (git-fileinfo->name info) file) - (unless (eq (git-fileinfo->state info) state) - (setf (git-fileinfo->state info) state) - (setf (git-fileinfo->rename-state info) nil) - (setf (git-fileinfo->orig-name info) nil) - (setf (git-fileinfo->needs-refresh info) t)) - (setq file (pop files))) - (t (setq file (pop files))))))) + (if (string-lessp (git-fileinfo->name info) file) + (setq node (ewoc-next status node)) + (if (string-equal (git-fileinfo->name info) file) + (apply func info args)) + (setq file (pop files)))))))) + +(defun git-set-filenames-state (status files state) + "Set the state of a list of named files." + (when files + (git-status-filenames-map status #'git-set-fileinfo-state files state) (unless state ;; delete files whose state has been set to nil (ewoc-filter status (lambda (info) (git-fileinfo->state info)))))) @@ -599,7 +599,7 @@ and returns the process output as a string." Return the list of files that haven't been handled." (let (infolist) (with-temp-buffer - (apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files) + (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) (while (re-search-forward ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" @@ -632,7 +632,7 @@ Return the list of files that haven't been handled." Return the list of files that haven't been handled." (let (infolist) (with-temp-buffer - (apply #'git-run-command t nil "ls-files" "-z" (append options (list "--") files)) + (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files)) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (let ((name (match-string 1))) @@ -644,7 +644,7 @@ Return the list of files that haven't been handled." (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." (with-temp-buffer - (apply #'git-run-command t nil "ls-files" "-z" "-u" "--" files) + (apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files) (goto-char (point-min)) (let (unmerged-files) (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) @@ -747,11 +747,11 @@ Return the list of files that haven't been handled." ('deleted (push info deleted)) ('modified (push info modified)))) (when added - (apply #'git-run-command nil env "update-index" "--add" "--" (git-get-filenames added))) + (apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added))) (when deleted - (apply #'git-run-command nil env "update-index" "--remove" "--" (git-get-filenames deleted))) + (apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted))) (when modified - (apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified))))) + (apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified))))) (defun git-run-pre-commit-hook () "Run the pre-commit hook if any." @@ -783,6 +783,7 @@ Return the list of files that haven't been handled." head-tree (git-rev-parse "HEAD^{tree}"))) (if files (progn + (message "Running git commit...") (git-read-tree head-tree index-file) (git-update-index nil files) ;update both the default index (git-update-index index-file files) ;and the temporary one @@ -793,8 +794,8 @@ Return the list of files that haven't been handled." (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) - (git-set-files-state files 'uptodate) - (git-run-command nil nil "rerere") + (dolist (info files) (git-set-fileinfo-state info 'uptodate)) + (git-call-process-env nil nil "rerere") (git-refresh-files) (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) @@ -905,8 +906,9 @@ Return the list of files that haven't been handled." (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored)))) (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) - (apply #'git-run-command nil nil "update-index" "--add" "--" files) - (git-update-status-files files 'uptodate))) + (apply #'git-call-process-env nil nil "update-index" "--add" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Added" files))) (defun git-ignore-file () "Add marked file(s) to the ignore list." @@ -915,7 +917,8 @@ Return the list of files that haven't been handled." (unless files (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files)) (dolist (f files) (git-append-to-ignore f)) - (git-update-status-files files 'ignored))) + (git-update-status-files files 'ignored) + (git-success-message "Ignored" files))) (defun git-remove-file () "Remove the marked file(s)." @@ -928,8 +931,9 @@ Return the list of files that haven't been handled." (progn (dolist (name files) (when (file-exists-p name) (delete-file name))) - (apply #'git-run-command nil nil "update-index" "--remove" "--" files) - (git-update-status-files files nil)) + (apply #'git-call-process-env nil nil "update-index" "--remove" "--" files) + (git-update-status-files files nil) + (git-success-message "Removed" files)) (message "Aborting")))) (defun git-revert-file () @@ -947,18 +951,20 @@ Return the list of files that haven't been handled." ('unmerged (push (git-fileinfo->name info) modified)) ('modified (push (git-fileinfo->name info) modified)))) (when added - (apply #'git-run-command nil nil "update-index" "--force-remove" "--" added)) + (apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added)) (when modified - (apply #'git-run-command nil nil "checkout" "HEAD" modified)) - (git-update-status-files (append added modified) 'uptodate)))) + (apply #'git-call-process-env nil nil "checkout" "HEAD" modified)) + (git-update-status-files (append added modified) 'uptodate) + (git-success-message "Reverted" files)))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." (interactive) (let ((files (git-get-filenames (git-marked-files-state 'unmerged)))) (when files - (apply #'git-run-command nil nil "update-index" "--" files) - (git-update-status-files files 'uptodate)))) + (apply #'git-call-process-env nil nil "update-index" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Resolved" files)))) (defun git-remove-handled () "Remove handled files from the status list." @@ -985,9 +991,11 @@ Return the list of files that haven't been handled." (interactive) (if (setq git-show-ignored (not git-show-ignored)) (progn + (message "Inserting ignored files...") (git-run-ls-files-with-excludes git-status nil 'ignored "-o" "-i") (git-refresh-files) - (git-refresh-ewoc-hf git-status)) + (git-refresh-ewoc-hf git-status) + (message "Inserting ignored files...done")) (git-remove-handled))) (defun git-toggle-show-unknown () @@ -995,9 +1003,11 @@ Return the list of files that haven't been handled." (interactive) (if (setq git-show-unknown (not git-show-unknown)) (progn + (message "Inserting unknown files...") (git-run-ls-files-with-excludes git-status nil 'unknown "-o") (git-refresh-files) - (git-refresh-ewoc-hf git-status)) + (git-refresh-ewoc-hf git-status) + (message "Inserting unknown files...done")) (git-remove-handled))) (defun git-setup-diff-buffer (buffer) @@ -1197,12 +1207,23 @@ Return the list of files that haven't been handled." (interactive) (let* ((status git-status) (pos (ewoc-locate status)) + (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info))))) (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) (unless status (error "Not in git-status buffer.")) - (git-run-command nil nil "update-index" "--refresh") + (message "Refreshing git status...") + (git-call-process-env nil nil "update-index" "--refresh") (git-clear-status status) (git-update-status-files nil) + ; restore file marks + (when marked-files + (git-status-filenames-map status + (lambda (info) + (setf (git-fileinfo->marked info) t) + (setf (git-fileinfo->needs-refresh info) t)) + marked-files) + (git-refresh-files)) ; move point to the current file name if any + (message "Refreshing git status...done") (let ((node (and cur-name (git-find-status-file status cur-name)))) (when node (ewoc-goto-node status node))))) @@ -1324,9 +1345,24 @@ Commands: (cd dir) (git-status-mode) (git-refresh-status) - (goto-char (point-min))) + (goto-char (point-min)) + (add-hook 'after-save-hook 'git-update-saved-file)) (message "%s is not a git working tree." dir))) +(defun git-update-saved-file () + "Update the corresponding git-status buffer when a file is saved. +Meant to be used in `after-save-hook'." + (let* ((file (expand-file-name buffer-file-name)) + (dir (condition-case nil (git-get-top-dir (file-name-directory file)))) + (buffer (and dir (git-find-status-buffer dir)))) + (when buffer + (with-current-buffer buffer + (let ((filename (file-relative-name file dir))) + ; skip files located inside the .git directory + (unless (string-match "^\\.git/" filename) + (git-call-process-env nil nil "add" "--refresh" "--" filename) + (git-update-status-files (list filename) 'uptodate))))))) + (defun git-help () "Display help for Git mode." (interactive) diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 1f88099df4..cbbd02fadd 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -177,7 +177,6 @@ generate_email_header() # --- Email (all stdout will be the email) # Generate header cat <<-EOF - From: $committer To: $recipients Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe X-Git-Refname: $refname @@ -584,6 +584,26 @@ int parse_date(const char *date, char *result, int maxlen) return date_string(then, offset, result, maxlen); } +enum date_mode parse_date_format(const char *format) +{ + if (!strcmp(format, "relative")) + return DATE_RELATIVE; + else if (!strcmp(format, "iso8601") || + !strcmp(format, "iso")) + return DATE_ISO8601; + else if (!strcmp(format, "rfc2822") || + !strcmp(format, "rfc")) + return DATE_RFC2822; + else if (!strcmp(format, "short")) + return DATE_SHORT; + else if (!strcmp(format, "local")) + return DATE_LOCAL; + else if (!strcmp(format, "default")) + return DATE_NORMAL; + else + die("unknown date format %s", format); +} + void datestamp(char *buf, int bufsize) { time_t now; @@ -1675,7 +1675,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) return 0; } -void diff_free_filespec_data(struct diff_filespec *s) +void diff_free_filespec_blob(struct diff_filespec *s) { if (s->should_free) free(s->data); @@ -1686,6 +1686,11 @@ void diff_free_filespec_data(struct diff_filespec *s) s->should_free = s->should_munmap = 0; s->data = NULL; } +} + +void diff_free_filespec_data(struct diff_filespec *s) +{ + diff_free_filespec_blob(s); free(s->cnt_data); s->cnt_data = NULL; } diff --git a/diffcore-rename.c b/diffcore-rename.c index 41b35c3a9e..142e5376dd 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -184,7 +184,8 @@ static int estimate_similarity(struct diff_filespec *src, if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE) return 0; - if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0)) + if ((!src->cnt_data && diff_populate_filespec(src, 0)) + || (!dst->cnt_data && diff_populate_filespec(dst, 0))) return 0; /* error but caught downstream */ @@ -377,10 +378,10 @@ void diffcore_rename(struct diff_options *options) m->score = estimate_similarity(one, two, minimum_score); m->name_score = basename_same(one, two); - diff_free_filespec_data(one); + diff_free_filespec_blob(one); } /* We do not need the text anymore */ - diff_free_filespec_data(two); + diff_free_filespec_blob(two); dst_cnt++; } /* cost matrix sorted by most to least similar pair */ diff --git a/diffcore.h b/diffcore.h index eef17c4ca2..eb618b1ec0 100644 --- a/diffcore.h +++ b/diffcore.h @@ -48,6 +48,7 @@ extern void fill_filespec(struct diff_filespec *, const unsigned char *, extern int diff_populate_filespec(struct diff_filespec *, int); extern void diff_free_filespec_data(struct diff_filespec *); +extern void diff_free_filespec_blob(struct diff_filespec *); extern int diff_filespec_is_binary(struct diff_filespec *); struct diff_filepair { diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 7921cde8cb..be6881496c 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -213,9 +213,13 @@ sub list_and_choose { print ">> "; } my $line = <STDIN>; - last if (!$line); + if (!$line) { + print "\n"; + $opts->{ON_EOF}->() if $opts->{ON_EOF}; + last; + } chomp $line; - my $donesomething = 0; + last if $line eq ''; for my $choice (split(/[\s,]+/, $line)) { my $choose = 1; my ($bottom, $top); @@ -247,12 +251,11 @@ sub list_and_choose { next TOPLOOP; } for ($i = $bottom-1; $i <= $top-1; $i++) { - next if (@stuff <= $i); + next if (@stuff <= $i || $i < 0); $chosen[$i] = $choose; - $donesomething++; } } - last if (!$donesomething || $opts->{IMMEDIATE}); + last if ($opts->{IMMEDIATE}); } for ($i = 0; $i < @stuff; $i++) { if ($chosen[$i]) { @@ -791,6 +794,7 @@ sub main_loop { SINGLETON => 1, LIST_FLAT => 4, HEADER => '*** Commands ***', + ON_EOF => \&quit_cmd, IMMEDIATE => 1 }, @cmd); if ($it) { eval { diff --git a/git-checkout.sh b/git-checkout.sh index 17f43927aa..8993920673 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -137,6 +137,13 @@ Did you intend to checkout '$@' which can not be resolved as commit?" git ls-files --error-unmatch -- "$@" >/dev/null || exit git ls-files -- "$@" | git checkout-index -f -u --stdin + + # Run a post-checkout hook -- the HEAD does not change so the + # current HEAD is passed in for both args + if test -x "$GIT_DIR"/hooks/post-checkout; then + "$GIT_DIR"/hooks/post-checkout $old $old 0 + fi + exit $? else # Make sure we did not fall back on $arg^{tree} codepath @@ -284,3 +291,8 @@ if [ "$?" -eq 0 ]; then else exit 1 fi + +# Run a post-checkout hook +if test -x "$GIT_DIR"/hooks/post-checkout; then + "$GIT_DIR"/hooks/post-checkout $old $new 1 +fi diff --git a/git-commit.sh b/git-commit.sh index 44ccc4418e..fcb8443bdf 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -25,6 +25,7 @@ refuse_partial () { exit 1 } +TMP_INDEX= THIS_INDEX="$GIT_DIR/index" NEXT_INDEX="$GIT_DIR/next-index$$" rm -f "$NEXT_INDEX" diff --git a/git-mergetool.sh b/git-mergetool.sh index a0e44f71c4..9f4f3134b6 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -12,6 +12,7 @@ USAGE='[--tool=tool] [file to merge] ...' SUBDIRECTORY_OK=Yes . git-sh-setup require_work_tree +prefix=$(git rev-parse --show-prefix) # Returns true if the mode reflects a symlink is_symlink () { @@ -162,9 +163,9 @@ merge_file () { local_mode=`git ls-files -u -- "$path" | awk '{if ($3==2) print $1;}'` remote_mode=`git ls-files -u -- "$path" | awk '{if ($3==3) print $1;}'` - base_present && git cat-file blob ":1:$path" > "$BASE" 2>/dev/null - local_present && git cat-file blob ":2:$path" > "$LOCAL" 2>/dev/null - remote_present && git cat-file blob ":3:$path" > "$REMOTE" 2>/dev/null + base_present && git cat-file blob ":1:$prefix$path" >"$BASE" 2>/dev/null + local_present && git cat-file blob ":2:$prefix$path" >"$LOCAL" 2>/dev/null + remote_present && git cat-file blob ":3:$prefix$path" >"$REMOTE" 2>/dev/null if test -z "$local_mode" -o -z "$remote_mode"; then echo "Deleted merge conflict for '$path':" @@ -191,10 +192,10 @@ merge_file () { case "$merge_tool" in kdiff3) if base_present ; then - (kdiff3 --auto --L1 "$path (Base)" -L2 "$path (Local)" --L3 "$path (Remote)" \ + (kdiff3 --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \ -o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1) else - (kdiff3 --auto -L1 "$path (Local)" --L2 "$path (Remote)" \ + (kdiff3 --auto --L1 "$path (Local)" --L2 "$path (Remote)" \ -o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1) fi status=$? @@ -251,9 +252,9 @@ merge_file () { ;; emerge) if base_present ; then - emacs -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$path" + emacs -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")" else - emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$path" + emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")" fi status=$? save_backup diff --git a/git-pull.sh b/git-pull.sh index c3f05f56de..74bfc16744 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -97,10 +97,24 @@ case "$merge_head" in esac curr_branch=${curr_branch#refs/heads/} - echo >&2 "Warning: No merge candidate found because value of config option - \"branch.${curr_branch}.merge\" does not match any remote branch fetched." - echo >&2 "No changes." - exit 0 + echo >&2 "You asked me to pull without telling me which branch you" + echo >&2 "want to merge with, and 'branch.${curr_branch}.merge' in" + echo >&2 "your configuration file does not tell me either. Please" + echo >&2 "name which branch you want to merge on the command line and" + echo >&2 "try again (e.g. 'git pull <repository> <refspec>')." + echo >&2 "See git-pull(1) for details on the refspec." + echo >&2 + echo >&2 "If you often merge with the same branch, you may want to" + echo >&2 "configure the following variables in your configuration" + echo >&2 "file:" + echo >&2 + echo >&2 " branch.${curr_branch}.remote = <nickname>" + echo >&2 " branch.${curr_branch}.merge = <remote-ref>" + echo >&2 " remote.<nickname>.url = <url>" + echo >&2 " remote.<nickname>.fetch = <refspec>" + echo >&2 + echo >&2 "See git-config(1) for details." + exit 1 ;; ?*' '?*) if test -z "$orig_head" diff --git a/git-quiltimport.sh b/git-quiltimport.sh index 74a54d5d08..880c81d121 100755 --- a/git-quiltimport.sh +++ b/git-quiltimport.sh @@ -71,6 +71,10 @@ commit=$(git rev-parse HEAD) mkdir $tmp_dir || exit 2 for patch_name in $(grep -v '^#' < "$QUILT_PATCHES/series" ); do + if ! [ -f "$QUILT_PATCHES/$patch_name" ] ; then + echo "$patch_name doesn't exist. Skipping." + continue + fi echo $patch_name git mailinfo "$tmp_msg" "$tmp_patch" \ <"$QUILT_PATCHES/$patch_name" >"$tmp_info" || exit 3 diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 823291d4af..050140d666 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -232,14 +232,14 @@ do_next () { '#'*|'') mark_action_done ;; - pick) + pick|p) comment_for_reflog pick mark_action_done pick_one $sha1 || die_with_patch $sha1 "Could not apply $sha1... $rest" ;; - edit) + edit|e) comment_for_reflog edit mark_action_done @@ -254,7 +254,7 @@ do_next () { warn exit 0 ;; - squash) + squash|s) comment_for_reflog squash has_action "$DONE" || @@ -263,7 +263,7 @@ do_next () { mark_action_done make_squash_message $sha1 > "$MSG" case "$(peek_next_command)" in - squash) + squash|s) EDIT_COMMIT= USE_OUTPUT=output cp "$MSG" "$SQUASH_MSG" @@ -276,9 +276,9 @@ do_next () { esac failed=f + author_script=$(get_author_ident_from_commit HEAD) output git reset --soft HEAD^ pick_one -n $sha1 || failed=t - author_script=$(get_author_ident_from_commit $sha1) echo "$author_script" > "$DOTEST"/author-script case $failed in f) diff --git a/git-remote.perl b/git-remote.perl index f6f283ea4f..9ca3e7ef37 100755 --- a/git-remote.perl +++ b/git-remote.perl @@ -218,7 +218,7 @@ sub prune_remote { my ($name, $ls_remote) = @_; if (!exists $remote->{$name}) { print STDERR "No such remote $name\n"; - return; + return 1; } my $info = $remote->{$name}; update_ls_remote($ls_remote, $info); @@ -229,13 +229,14 @@ sub prune_remote { my @v = $git->command(qw(rev-parse --verify), "$prefix/$to_prune"); $git->command(qw(update-ref -d), "$prefix/$to_prune", $v[0]); } + return 0; } sub show_remote { my ($name, $ls_remote) = @_; if (!exists $remote->{$name}) { print STDERR "No such remote $name\n"; - return; + return 1; } my $info = $remote->{$name}; update_ls_remote($ls_remote, $info); @@ -265,6 +266,7 @@ sub show_remote { print " Local branch(es) pushed with 'git push'\n"; print " @pushed\n"; } + return 0; } sub add_remote { @@ -316,6 +318,34 @@ sub update_remote { } } +sub rm_remote { + my ($name) = @_; + if (!exists $remote->{$name}) { + print STDERR "No such remote $name\n"; + return 1; + } + + $git->command('config', '--remove-section', "remote.$name"); + + eval { + my @trackers = $git->command('config', '--get-regexp', + 'branch.*.remote', $name); + for (@trackers) { + /^branch\.(.*)?\.remote/; + $git->config('--unset', "branch.$1.remote"); + $git->config('--unset', "branch.$1.merge"); + } + }; + + my @refs = $git->command('for-each-ref', + '--format=%(refname) %(objectname)', "refs/remotes/$name"); + for (@refs) { + ($ref, $object) = split; + $git->command(qw(update-ref -d), $ref, $object); + } + return 0; +} + sub add_usage { print STDERR "Usage: git remote add [-f] [-t track]* [-m master] <name> <url>\n"; exit(1); @@ -353,9 +383,11 @@ elsif ($ARGV[0] eq 'show') { print STDERR "Usage: git remote show <remote>\n"; exit(1); } + my $status = 0; for (; $i < @ARGV; $i++) { - show_remote($ARGV[$i], $ls_remote); + $status |= show_remote($ARGV[$i], $ls_remote); } + exit($status); } elsif ($ARGV[0] eq 'update') { if (@ARGV <= 1) { @@ -381,9 +413,11 @@ elsif ($ARGV[0] eq 'prune') { print STDERR "Usage: git remote prune <remote>\n"; exit(1); } + my $status = 0; for (; $i < @ARGV; $i++) { - prune_remote($ARGV[$i], $ls_remote); + $status |= prune_remote($ARGV[$i], $ls_remote); } + exit($status); } elsif ($ARGV[0] eq 'add') { my %opts = (); @@ -422,9 +456,17 @@ elsif ($ARGV[0] eq 'add') { } add_remote($ARGV[1], $ARGV[2], \%opts); } +elsif ($ARGV[0] eq 'rm') { + if (@ARGV <= 1) { + print STDERR "Usage: git remote rm <remote>\n"; + exit(1); + } + exit(rm_remote($ARGV[1])); +} else { print STDERR "Usage: git remote\n"; print STDERR " git remote add <name> <url>\n"; + print STDERR " git remote rm <name>\n"; print STDERR " git remote show <name>\n"; print STDERR " git remote prune <name>\n"; print STDERR " git remote update [group]\n"; diff --git a/git-send-email.perl b/git-send-email.perl index 4031e86b86..62e1429733 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -77,7 +77,10 @@ Options: the default section. --smtp-server If set, specifies the outgoing SMTP server to use. - Defaults to localhost. + Defaults to localhost. Port number can be specified here with + hostname:port format or by using --smtp-server-port option. + + --smtp-server-port Specify a port on the outgoing SMTP server to connect to. --smtp-user The username for SMTP-AUTH. @@ -172,8 +175,8 @@ my ($quiet, $dry_run) = (0, 0); # Variables with corresponding config settings my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd); -my ($smtp_server, $smtp_authuser, $smtp_authpass, $smtp_ssl); -my ($identity, $aliasfiletype, @alias_files); +my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_authpass, $smtp_ssl); +my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts); my %config_bool_settings = ( "thread" => [\$thread, 1], @@ -185,6 +188,7 @@ my %config_bool_settings = ( my %config_settings = ( "smtpserver" => \$smtp_server, + "smtpserverport" => \$smtp_server_port, "smtpuser" => \$smtp_authuser, "smtppass" => \$smtp_authpass, "cccmd" => \$cc_cmd, @@ -204,6 +208,7 @@ my $rc = GetOptions("sender|from=s" => \$sender, "bcc=s" => \@bcclist, "chain-reply-to!" => \$chain_reply_to, "smtp-server=s" => \$smtp_server, + "smtp-server-port=s" => \$smtp_server_port, "smtp-user=s" => \$smtp_authuser, "smtp-pass=s" => \$smtp_authpass, "smtp-ssl!" => \$smtp_ssl, @@ -602,16 +607,30 @@ X-Mailer: git-send-email $gitversion print $sm "$header\n$message"; close $sm or die $?; } else { + + if (!defined $smtp_server) { + die "The required SMTP server is not properly defined." + } + if ($smtp_ssl) { + $smtp_server_port ||= 465; # ssmtp require Net::SMTP::SSL; - $smtp ||= Net::SMTP::SSL->new( $smtp_server, Port => 465 ); + $smtp ||= Net::SMTP::SSL->new($smtp_server, Port => $smtp_server_port); } else { require Net::SMTP; - $smtp ||= Net::SMTP->new( $smtp_server ); + $smtp ||= Net::SMTP->new((defined $smtp_server_port) + ? "$smtp_server:$smtp_server_port" + : $smtp_server); + } + + if (!$smtp) { + die "Unable to initialize SMTP properly. Is there something wrong with your config?"; + } + + if ((defined $smtp_authuser) && (defined $smtp_authpass)) { + $smtp->auth( $smtp_authuser, $smtp_authpass ) or die $smtp->message; } - $smtp->auth( $smtp_authuser, $smtp_authpass ) - or die $smtp->message if (defined $smtp_authuser); $smtp->mail( $raw_from ) or die $smtp->message; $smtp->to( @recipients ) or die $smtp->message; $smtp->data or die $smtp->message; diff --git a/git-submodule.sh b/git-submodule.sh index 673aa27a45..4aaaaab0d8 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -39,6 +39,32 @@ get_repo_base() { ) 2>/dev/null } +# Resolve relative url by appending to parent's url +resolve_relative_url () +{ + branch="$(git symbolic-ref HEAD 2>/dev/null)" + remote="$(git config branch.${branch#refs/heads/}.remote)" + remote="${remote:-origin}" + remoteurl="$(git config remote.$remote.url)" || + die "remote ($remote) does not have a url in .git/config" + url="$1" + while test -n "$url" + do + case "$url" in + ../*) + url="${url#../}" + remoteurl="${remoteurl%/*}" + ;; + ./*) + url="${url#./}" + ;; + *) + break;; + esac + done + echo "$remoteurl/$url" +} + # # Map submodule path to submodule name # @@ -103,11 +129,19 @@ module_add() usage fi - # Turn the source into an absolute path if - # it is local - if base=$(get_repo_base "$repo"); then - repo="$base" - fi + case "$repo" in + ./*|../*) + # dereference source url relative to parent's url + realrepo="$(resolve_relative_url $repo)" ;; + *) + # Turn the source into an absolute path if + # it is local + if base=$(get_repo_base "$repo"); then + repo="$base" + fi + realrepo=$repo + ;; + esac # Guess path from repo if not specified or strip trailing slashes if test -z "$path"; then @@ -122,7 +156,7 @@ module_add() git ls-files --error-unmatch "$path" > /dev/null 2>&1 && die "'$path' already exists in the index" - module_clone "$path" "$repo" || exit + module_clone "$path" "$realrepo" || exit (unset GIT_DIR && cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) || die "Unable to checkout submodule '$path'" git add "$path" || @@ -153,6 +187,13 @@ modules_init() test -z "$url" && die "No url found for submodule path '$path' in .gitmodules" + # Possibly a url relative to parent + case "$url" in + ./*|../*) + url="$(resolve_relative_url "$url")" + ;; + esac + git config submodule."$name".url "$url" || die "Failed to register url for submodule path '$path'" diff --git a/git-svnimport.perl b/git-svnimport.perl index aa5b3b2c97..ea8c1b2f60 100755 --- a/git-svnimport.perl +++ b/git-svnimport.perl @@ -54,6 +54,7 @@ my $branch_name = $opt_b || "branches"; my $project_name = $opt_P || ""; $project_name = "/" . $project_name if ($project_name); my $repack_after = $opt_R || 1000; +my $root_pool = SVN::Pool->new_default; @ARGV == 1 or @ARGV == 2 or usage(); @@ -132,7 +133,7 @@ sub conn { my $auth = SVN::Core::auth_open ([SVN::Client::get_simple_provider, SVN::Client::get_ssl_server_trust_file_provider, SVN::Client::get_username_provider]); - my $s = SVN::Ra->new(url => $repo, auth => $auth); + my $s = SVN::Ra->new(url => $repo, auth => $auth, pool => $root_pool); die "SVN connection to $repo: $!\n" unless defined $s; $self->{'svn'} = $s; $self->{'repo'} = $repo; @@ -147,11 +148,10 @@ sub file { print "... $rev $path ...\n" if $opt_v; my (undef, $properties); - my $pool = SVN::Pool->new(); $path =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; eval { (undef, $properties) - = $self->{'svn'}->get_file($path,$rev,$fh,$pool); }; - $pool->clear; + = $self->{'svn'}->get_file($path,$rev,$fh); }; if($@) { return undef if $@ =~ /Attempted to get checksum/; die $@; @@ -185,6 +185,7 @@ sub ignore { print "... $rev $path ...\n" if $opt_v; $path =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; my (undef,undef,$properties) = $self->{'svn'}->get_dir($path,$rev,undef); if (exists $properties->{'svn:ignore'}) { @@ -202,6 +203,7 @@ sub ignore { sub dir_list { my($self,$path,$rev) = @_; $path =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; my ($dirents,undef,$properties) = $self->{'svn'}->get_dir($path,$rev,undef); return $dirents; @@ -358,10 +360,9 @@ open BRANCHES,">>", "$git_dir/svn2git"; sub node_kind($$) { my ($svnpath, $revision) = @_; - my $pool=SVN::Pool->new; $svnpath =~ s#^/*##; - my $kind = $svn->{'svn'}->check_path($svnpath,$revision,$pool); - $pool->clear; + my $subpool = SVN::Pool::new_default_sub; + my $kind = $svn->{'svn'}->check_path($svnpath,$revision); return $kind; } @@ -889,7 +890,7 @@ sub commit_all { # Recursive use of the SVN connection does not work local $svn = $svn2; - my ($changed_paths, $revision, $author, $date, $message, $pool) = @_; + my ($changed_paths, $revision, $author, $date, $message) = @_; my %p; while(my($path,$action) = each %$changed_paths) { $p{$path} = [ $action->action,$action->copyfrom_path, $action->copyfrom_rev, $path ]; @@ -925,14 +926,14 @@ print "Processing from $current_rev to $opt_l ...\n" if $opt_v; my $from_rev; my $to_rev = $current_rev - 1; +my $subpool = SVN::Pool::new_default_sub; while ($to_rev < $opt_l) { + $subpool->clear; $from_rev = $to_rev + 1; $to_rev = $from_rev + $repack_after; $to_rev = $opt_l if $opt_l < $to_rev; print "Fetching from $from_rev to $to_rev ...\n" if $opt_v; - my $pool=SVN::Pool->new; - $svn->{'svn'}->get_log("/",$from_rev,$to_rev,0,1,1,\&commit_all,$pool); - $pool->clear; + $svn->{'svn'}->get_log("/",$from_rev,$to_rev,0,1,1,\&commit_all); my $pid = fork(); die "Fork: $!\n" unless defined $pid; unless($pid) { diff --git a/revision.c b/revision.c index 33d092c3c4..5d294be308 100644 --- a/revision.c +++ b/revision.c @@ -1134,22 +1134,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch continue; } if (!strncmp(arg, "--date=", 7)) { - if (!strcmp(arg + 7, "relative")) - revs->date_mode = DATE_RELATIVE; - else if (!strcmp(arg + 7, "iso8601") || - !strcmp(arg + 7, "iso")) - revs->date_mode = DATE_ISO8601; - else if (!strcmp(arg + 7, "rfc2822") || - !strcmp(arg + 7, "rfc")) - revs->date_mode = DATE_RFC2822; - else if (!strcmp(arg + 7, "short")) - revs->date_mode = DATE_SHORT; - else if (!strcmp(arg + 7, "local")) - revs->date_mode = DATE_LOCAL; - else if (!strcmp(arg + 7, "default")) - revs->date_mode = DATE_NORMAL; - else - die("unknown date format %s", arg); + revs->date_mode = parse_date_format(arg + 7); continue; } if (!strcmp(arg, "--log-size")) { @@ -1209,8 +1194,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i); if (opts > 0) { - if (strcmp(argv[i], "-z")) - revs->diff = 1; i += opts - 1; continue; } @@ -1254,6 +1237,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch add_pending_object_with_mode(revs, object, def, mode); } + /* Did the user ask for any diff output? Run the diff! */ + if (revs->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) + revs->diff = 1; + + /* Pickaxe needs diffs */ + if (revs->diffopt.pickaxe) + revs->diff = 1; + if (revs->topo_order) revs->limited = 1; diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh new file mode 100755 index 0000000000..68eb266d73 --- /dev/null +++ b/t/t3060-ls-files-with-tree.sh @@ -0,0 +1,71 @@ +#!/bin/sh +# +# Copyright (c) 2007 Carl D. Worth +# + +test_description='git ls-files test (--with-tree). + +This test runs git ls-files --with-tree and in particular in +a scenario known to trigger a crash with some versions of git. +' +. ./test-lib.sh + +test_expect_success setup ' + + # The bug we are exercising requires a fair number of entries + # in a sub-directory so that add_index_entry will trigger a + # realloc. + + echo file >expected && + mkdir sub && + bad= && + for n in 0 1 2 3 4 5 + do + for m in 0 1 2 3 4 5 6 7 8 9 + do + num=00$n$m && + >sub/file-$num && + echo file-$num >>expected || { + bad=t + break + } + done && test -z "$bad" || { + bad=t + break + } + done && test -z "$bad" && + git add . && + git commit -m "add a bunch of files" && + + # We remove them all so that we will have something to add + # back with --with-tree and so that we will definitely be + # under the realloc size to trigger the bug. + rm -rf sub && + git commit -a -m "remove them all" && + + # The bug also requires some entry before our directory so that + # prune_path will modify the_index.cache + + mkdir a_directory_that_sorts_before_sub && + >a_directory_that_sorts_before_sub/file && + mkdir sub && + >sub/file && + git add . +' + +# We have to run from a sub-directory to trigger prune_path +# Then we finally get to run our --with-tree test +cd sub + +test_expect_success 'git -ls-files --with-tree should succeed from subdir' ' + + git ls-files --with-tree=HEAD~1 >../output + +' + +cd .. +test_expect_success \ + 'git -ls-files --with-tree should add entries from named tree.' \ + 'diff -u expected output' + +test_done diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index f2214dd0fa..11139048fe 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -180,7 +180,7 @@ test_expect_success 'squash' ' ' test_expect_success 'retain authorship when squashing' ' - git show HEAD | grep "^Author: Nitfol" + git show HEAD | grep "^Author: Twerp Snog" ' test_expect_success 'preserve merges with -p' ' diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh new file mode 100755 index 0000000000..487abf3fc6 --- /dev/null +++ b/t/t5403-post-checkout-hook.sh @@ -0,0 +1,74 @@ +#!/bin/sh +# +# Copyright (c) 2006 Josh England +# + +test_description='Test the post-checkout hook.' +. ./test-lib.sh + +test_expect_success setup ' + echo Data for commit0. >a && + echo Data for commit0. >b && + git update-index --add a && + git update-index --add b && + tree0=$(git write-tree) && + commit0=$(echo setup | git commit-tree $tree0) && + git update-ref refs/heads/master $commit0 && + git-clone ./. clone1 && + git-clone ./. clone2 && + GIT_DIR=clone2/.git git branch -a new2 && + echo Data for commit1. >clone2/b && + GIT_DIR=clone2/.git git add clone2/b && + GIT_DIR=clone2/.git git commit -m new2 +' + +for clone in 1 2; do + cat >clone${clone}/.git/hooks/post-checkout <<'EOF' +#!/bin/sh +echo $@ > $GIT_DIR/post-checkout.args +EOF + chmod u+x clone${clone}/.git/hooks/post-checkout +done + +test_expect_success 'post-checkout runs as expected ' ' + GIT_DIR=clone1/.git git checkout master && + test -e clone1/.git/post-checkout.args +' + +test_expect_success 'post-checkout receives the right arguments with HEAD unchanged ' ' + old=$(awk "{print \$1}" clone1/.git/post-checkout.args) && + new=$(awk "{print \$2}" clone1/.git/post-checkout.args) && + flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) && + test $old = $new -a $flag == 1 +' + +test_expect_success 'post-checkout runs as expected ' ' + GIT_DIR=clone1/.git git checkout master && + test -e clone1/.git/post-checkout.args +' + +test_expect_success 'post-checkout args are correct with git checkout -b ' ' + GIT_DIR=clone1/.git git checkout -b new1 && + old=$(awk "{print \$1}" clone1/.git/post-checkout.args) && + new=$(awk "{print \$2}" clone1/.git/post-checkout.args) && + flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) && + test $old = $new -a $flag == 1 +' + +test_expect_success 'post-checkout receives the right args with HEAD changed ' ' + GIT_DIR=clone2/.git git checkout new2 && + old=$(awk "{print \$1}" clone2/.git/post-checkout.args) && + new=$(awk "{print \$2}" clone2/.git/post-checkout.args) && + flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) && + test $old != $new -a $flag == 1 +' + +test_expect_success 'post-checkout receives the right args when not switching branches ' ' + GIT_DIR=clone2/.git git checkout master b && + old=$(awk "{print \$1}" clone2/.git/post-checkout.args) && + new=$(awk "{print \$2}" clone2/.git/post-checkout.args) && + flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) && + test $old == $new -a $flag == 0 +' + +test_done diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh new file mode 100755 index 0000000000..636aec2f71 --- /dev/null +++ b/t/t5505-remote.sh @@ -0,0 +1,100 @@ +#!/bin/sh + +test_description='git remote porcelain-ish' + +. ./test-lib.sh + +GIT_CONFIG=.git/config +export GIT_CONFIG + +setup_repository () { + mkdir "$1" && ( + cd "$1" && + git init && + >file && + git add file && + git commit -m "Initial" && + git checkout -b side && + >elif && + git add elif && + git commit -m "Second" && + git checkout master + ) +} + +tokens_match () { + echo "$1" | tr ' ' '\012' | sort | sed -e '/^$/d' >expect && + echo "$2" | tr ' ' '\012' | sort | sed -e '/^$/d' >actual && + diff -u expect actual +} + +check_remote_track () { + actual=$(git remote show "$1" | sed -n -e '$p') && + shift && + tokens_match "$*" "$actual" +} + +check_tracking_branch () { + f="" && + r=$(git for-each-ref "--format=%(refname)" | + sed -ne "s|^refs/remotes/$1/||p") && + shift && + tokens_match "$*" "$r" +} + +test_expect_success setup ' + + setup_repository one && + setup_repository two && + ( + cd two && git branch another + ) && + git clone one test + +' + +test_expect_success 'remote information for the origin' ' +( + cd test && + tokens_match origin "$(git remote)" && + check_remote_track origin master side && + check_tracking_branch origin HEAD master side +) +' + +test_expect_success 'add another remote' ' +( + cd test && + git remote add -f second ../two && + tokens_match "origin second" "$(git remote)" && + check_remote_track origin master side && + check_remote_track second master side another && + check_tracking_branch second master side another && + git for-each-ref "--format=%(refname)" refs/remotes | + sed -e "/^refs\/remotes\/origin\//d" \ + -e "/^refs\/remotes\/second\//d" >actual && + >expect && + diff -u expect actual +) +' + +test_expect_success 'remove remote' ' +( + cd test && + git remote rm second +) +' + +test_expect_success 'remove remote' ' +( + cd test && + tokens_match origin "$(git remote)" && + check_remote_track origin master side && + git for-each-ref "--format=%(refname)" refs/remotes | + sed -e "/^refs\/remotes\/origin\//d" >actual && + >expect && + diff -u expect actual +) +' + +test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh new file mode 100644 index 0000000000..d0809eb651 --- /dev/null +++ b/t/t6300-for-each-ref.sh @@ -0,0 +1,151 @@ +#!/bin/sh +# +# Copyright (c) 2007 Andy Parkins +# + +test_description='for-each-ref test' + +. ./test-lib.sh + +# Mon Jul 3 15:18:43 2006 +0000 +datestamp=1151939923 +setdate_and_increment () { + GIT_COMMITTER_DATE="$datestamp +0200" + datestamp=$(expr "$datestamp" + 1) + GIT_AUTHOR_DATE="$datestamp +0200" + datestamp=$(expr "$datestamp" + 1) + export GIT_COMMITTER_DATE GIT_AUTHOR_DATE +} + +test_expect_success 'Create sample commit with known timestamp' ' + setdate_and_increment && + echo "Using $datestamp" > one && + git add one && + git commit -m "Initial" && + setdate_and_increment && + git tag -a -m "Tagging at $datestamp" testtag +' + +test_expect_success 'Check atom names are valid' ' + bad= + for token in \ + refname objecttype objectsize objectname tree parent \ + numparent object type author authorname authoremail \ + authordate committer committername committeremail \ + committerdate tag tagger taggername taggeremail \ + taggerdate creator creatordate subject body contents + do + git for-each-ref --format="$token=%($token)" refs/heads || { + bad=$token + break + } + done + test -z "$bad" +' + +test_expect_failure 'Check invalid atoms names are errors' ' + git-for-each-ref --format="%(INVALID)" refs/heads +' + +test_expect_success 'Check format specifiers are ignored in naming date atoms' ' + git-for-each-ref --format="%(authordate)" refs/heads && + git-for-each-ref --format="%(authordate:default) %(authordate)" refs/heads && + git-for-each-ref --format="%(authordate) %(authordate:default)" refs/heads && + git-for-each-ref --format="%(authordate:default) %(authordate:default)" refs/heads +' + +test_expect_success 'Check valid format specifiers for date fields' ' + git-for-each-ref --format="%(authordate:default)" refs/heads && + git-for-each-ref --format="%(authordate:relative)" refs/heads && + git-for-each-ref --format="%(authordate:short)" refs/heads && + git-for-each-ref --format="%(authordate:local)" refs/heads && + git-for-each-ref --format="%(authordate:iso8601)" refs/heads && + git-for-each-ref --format="%(authordate:rfc2822)" refs/heads +' + +test_expect_failure 'Check invalid format specifiers are errors' ' + git-for-each-ref --format="%(authordate:INVALID)" refs/heads +' + +cat >expected <<\EOF +'refs/heads/master' 'Mon Jul 3 17:18:43 2006 +0200' 'Mon Jul 3 17:18:44 2006 +0200' +'refs/tags/testtag' 'Mon Jul 3 17:18:45 2006 +0200' +EOF + +test_expect_success 'Check unformatted date fields output' ' + (git for-each-ref --shell --format="%(refname) %(committerdate) %(authordate)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate)" refs/tags) >actual && + git diff expected actual +' + +test_expect_success 'Check format "default" formatted date fields output' ' + f=default && + (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && + git diff expected actual +' + +# Don't know how to do relative check because I can't know when this script +# is going to be run and can't fake the current time to git, and hence can't +# provide expected output. Instead, I'll just make sure that "relative" +# doesn't exit in error +# +#cat >expected <<\EOF +# +#EOF +# +test_expect_success 'Check format "relative" date fields output' ' + f=relative && + (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual +' + +cat >expected <<\EOF +'refs/heads/master' '2006-07-03' '2006-07-03' +'refs/tags/testtag' '2006-07-03' +EOF + +test_expect_success 'Check format "short" date fields output' ' + f=short && + (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && + git diff expected actual +' + +cat >expected <<\EOF +'refs/heads/master' 'Mon Jul 3 15:18:43 2006' 'Mon Jul 3 15:18:44 2006' +'refs/tags/testtag' 'Mon Jul 3 15:18:45 2006' +EOF + +test_expect_success 'Check format "local" date fields output' ' + f=local && + (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && + git diff expected actual +' + +cat >expected <<\EOF +'refs/heads/master' '2006-07-03 17:18:43 +0200' '2006-07-03 17:18:44 +0200' +'refs/tags/testtag' '2006-07-03 17:18:45 +0200' +EOF + +test_expect_success 'Check format "iso8601" date fields output' ' + f=iso8601 && + (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && + git diff expected actual +' + +cat >expected <<\EOF +'refs/heads/master' 'Mon, 3 Jul 2006 17:18:43 +0200' 'Mon, 3 Jul 2006 17:18:44 +0200' +'refs/tags/testtag' 'Mon, 3 Jul 2006 17:18:45 +0200' +EOF + +test_expect_success 'Check format "rfc2822" date fields output' ' + f=rfc2822 && + (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && + git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual && + git diff expected actual +' + +test_done diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh index 622ea1c0df..5aac644223 100755 --- a/t/t9101-git-svn-props.sh +++ b/t/t9101-git-svn-props.sh @@ -140,6 +140,7 @@ test_expect_success 'test show-ignore' " cd test_wc && mkdir -p deeply/nested/directory && svn add deeply && + svn up && svn propset -R svn:ignore 'no-such-file*' . svn commit -m 'propset svn:ignore' cd .. && diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh index d8f9cab35d..7ba76309ac 100755 --- a/t/t9104-git-svn-follow-parent.sh +++ b/t/t9104-git-svn-follow-parent.sh @@ -19,8 +19,7 @@ test_expect_success 'initialize repo' " poke trunk/readme && svn commit -m 'another commit' && svn up && - svn mv -m 'rename to thunk' trunk thunk && - svn up && + svn mv trunk thunk && echo goodbye >> thunk/readme && poke thunk/readme && svn commit -m 'bye now' && @@ -52,8 +51,10 @@ test_expect_success 'init and fetch from one svn-remote' " " test_expect_success 'follow deleted parent' " - svn cp -m 'resurrecting trunk as junk' \ - -r2 $svnrepo/trunk $svnrepo/junk && + (svn cp -m 'resurrecting trunk as junk' \ + $svnrepo/trunk@2 $svnrepo/junk || + svn cp -m 'resurrecting trunk as junk' \ + -r2 $svnrepo/trunk $svnrepo/junk) && git config --add svn-remote.svn.fetch \ junk:refs/remotes/svn/junk && git-svn fetch -i svn/thunk && |