diff options
31 files changed, 502 insertions, 124 deletions
diff --git a/Documentation/RelNotes-1.5.3.3.txt b/Documentation/RelNotes-1.5.3.3.txt index e91bd84162..2a7bfdd5cc 100644 --- a/Documentation/RelNotes-1.5.3.3.txt +++ b/Documentation/RelNotes-1.5.3.3.txt @@ -29,9 +29,3 @@ Fixes since v1.5.3.2 * git-log sometimes invoked underlying "diff" machinery unnecessarily. - --- -exec >/var/tmp/1 -O=v1.5.3.2-29-gb7bb760 -echo O=`git describe refs/heads/maint` -git shortlog --no-merges $O..refs/heads/maint 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 015910f27a..f59fbb3024 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -579,7 +579,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/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-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-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-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.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]. @@ -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_large(struct diff_filespec *s) +void diff_free_filespec_blob(struct diff_filespec *s) { if (s->should_free) free(s->data); @@ -1690,7 +1690,7 @@ void diff_free_filespec_data_large(struct diff_filespec *s) void diff_free_filespec_data(struct diff_filespec *s) { - diff_free_filespec_data_large(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 4fc200064a..142e5376dd 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -378,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_large(one); + diff_free_filespec_blob(one); } /* We do not need the text anymore */ - diff_free_filespec_data_large(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 4bf175bda9..eb618b1ec0 100644 --- a/diffcore.h +++ b/diffcore.h @@ -48,7 +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_data_large(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-commit.sh b/git-commit.sh index cb14f06216..c3b881bfad 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-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-rebase--interactive.sh b/git-rebase--interactive.sh index 268a629c43..8568a4fd42 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 b7c1e01d7d..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 { @@ -320,7 +322,7 @@ sub rm_remote { my ($name) = @_; if (!exists $remote->{$name}) { print STDERR "No such remote $name\n"; - return; + return 1; } $git->command('config', '--remove-section', "remote.$name"); @@ -335,13 +337,13 @@ sub rm_remote { } }; - 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 { @@ -381,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) { @@ -409,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 = (); @@ -455,7 +461,7 @@ elsif ($ARGV[0] eq 'rm') { print STDERR "Usage: git remote rm <remote>\n"; exit(1); } - rm_remote($ARGV[1]); + exit(rm_remote($ARGV[1])); } else { print STDERR "Usage: git remote\n"; diff --git a/git-submodule.sh b/git-submodule.sh index 727b1d3206..4aaaaab0d8 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -138,8 +138,8 @@ module_add() # it is local if base=$(get_repo_base "$repo"); then repo="$base" - realrepo=$repo fi + realrepo=$repo ;; esac diff --git a/revision.c b/revision.c index 658471385c..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")) { 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 1af73a47c6..f5ef8c2258 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/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 |