Merge branch 'kh/commit' into wc/add-i
authorJunio C Hamano <gitster@pobox.com>
Sun, 25 Nov 2007 16:46:29 +0000 (08:46 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 25 Nov 2007 16:46:29 +0000 (08:46 -0800)
This is to use a few functions refactored to use in the built-in
commit series.

* kh/commit: (28 commits)
Add a few more tests for git-commit
builtin-commit: Include the diff in the commit message when verbose.
builtin-commit: fix partial-commit support
Fix add_files_to_cache() to take pathspec, not user specified list of files
Export three helper functions from ls-files
builtin-commit: run commit-msg hook with correct message file
builtin-commit: do not color status output shown in the message template
file_exists(): dangling symlinks do exist
Replace "runstatus" with "status" in the tests
t7501-commit: Add test for git commit <file> with dirty index.
builtin-commit: Clean up an unused variable and a debug fprintf().
Call refresh_cache() when updating the user index for --only commits.
builtin-commit: Add newline when showing which commit was created
builtin-commit: resurrect behavior for multiple -m options
builtin-commit --s: add a newline if the last line was not a S-o-b
builtin-commit: fix --signoff
git status: show relative paths when run in a subdirectory
builtin-commit: Refresh cache after adding files.
builtin-commit: fix reflog message generation
launch_editor(): read the file, even when EDITOR=:
...

33 files changed:
.gitignore
Makefile
builtin-add.c
builtin-commit.c [new file with mode: 0644]
builtin-ls-files.c
builtin-merge-ours.c [new file with mode: 0644]
builtin-runstatus.c
builtin-tag.c
builtin.h
bundle.c
cache.h
commit.h
contrib/examples/git-commit.sh [new file with mode: 0755]
contrib/examples/git-merge-ours.sh [new file with mode: 0755]
dir.c
git-commit.sh [deleted file]
git-merge-ours.sh [deleted file]
git-rebase--interactive.sh
git.c
ident.c
strbuf.h
t/t3001-ls-files-others-exclude.sh
t/t3404-rebase-interactive.sh
t/t4001-diff-rename.sh
t/t4018-diff-funcname.sh [changed mode: 0644->0755]
t/t5510-fetch.sh
t/t7500-commit.sh
t/t7501-commit.sh [changed mode: 0644->0755]
t/t7502-commit.sh [new file with mode: 0755]
t/t7502-status.sh [new file with mode: 0755]
t/t9119-git-svn-info.sh [changed mode: 0644->0755]
wt-status.c
wt-status.h
index c8c13f550317e12ccb5d3515fbc942463c8be4e0..bbd7f558e71ef05f2a99df1e7a38a06b10c0d03c 100644 (file)
@@ -109,7 +109,6 @@ git-rev-list
 git-rev-parse
 git-revert
 git-rm
-git-runstatus
 git-send-email
 git-send-pack
 git-sh-setup
index cabde8177e1dd2ad03c558339f932b6959a9bce2..35f9c873c4422f12c5078d5df248930deee4ff10 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -213,7 +213,7 @@ BASIC_LDFLAGS =
 
 SCRIPT_SH = \
        git-bisect.sh git-checkout.sh \
-       git-clean.sh git-clone.sh git-commit.sh \
+       git-clean.sh git-clone.sh \
        git-ls-remote.sh \
        git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
        git-pull.sh git-rebase.sh git-rebase--interactive.sh \
@@ -221,7 +221,7 @@ SCRIPT_SH = \
        git-sh-setup.sh \
        git-am.sh \
        git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
-       git-merge-resolve.sh git-merge-ours.sh \
+       git-merge-resolve.sh \
        git-lost-found.sh git-quiltimport.sh git-submodule.sh \
        git-filter-branch.sh \
        git-stash.sh
@@ -260,7 +260,7 @@ EXTRA_PROGRAMS =
 BUILT_INS = \
        git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
        git-get-tar-commit-id$X git-init$X git-repo-config$X \
-       git-fsck-objects$X git-cherry-pick$X \
+       git-fsck-objects$X git-cherry-pick$X git-status$X\
        $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
 
 # what 'all' will build and 'install' will install, in gitexecdir
@@ -330,6 +330,7 @@ BUILTIN_OBJS = \
        builtin-check-attr.o \
        builtin-checkout-index.o \
        builtin-check-ref-format.o \
+       builtin-commit.o \
        builtin-commit-tree.o \
        builtin-count-objects.o \
        builtin-describe.o \
@@ -353,6 +354,7 @@ BUILTIN_OBJS = \
        builtin-mailsplit.o \
        builtin-merge-base.o \
        builtin-merge-file.o \
+       builtin-merge-ours.o \
        builtin-mv.o \
        builtin-name-rev.o \
        builtin-pack-objects.o \
@@ -368,7 +370,6 @@ BUILTIN_OBJS = \
        builtin-rev-parse.o \
        builtin-revert.o \
        builtin-rm.o \
-       builtin-runstatus.o \
        builtin-shortlog.o \
        builtin-show-branch.o \
        builtin-stripspace.o \
@@ -837,9 +838,6 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
        chmod +x $@+ && \
        mv $@+ $@
 
-git-status: git-commit
-       $(QUIET_GEN)cp $< $@+ && mv $@+ $@
-
 gitweb/gitweb.cgi: gitweb/gitweb.perl
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
index 9e3beafe5b2a3d1a547c2c3f53bdedbaf00d280e..dd895dfb1d35f7e90d6529821e284d73e912d6d6 100644 (file)
@@ -105,12 +105,12 @@ static void update_callback(struct diff_queue_struct *q,
        }
 }
 
-void add_files_to_cache(int verbose, const char *prefix, const char **files)
+void add_files_to_cache(int verbose, const char *prefix, const char **pathspec)
 {
        struct rev_info rev;
        init_revisions(&rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
-       rev.prune_data = get_pathspec(prefix, files);
+       rev.prune_data = pathspec;
        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = update_callback;
        rev.diffopt.format_callback_data = &verbose;
@@ -183,9 +183,11 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        newfd = hold_locked_index(&lock_file, 1);
 
        if (take_worktree_changes) {
+               const char **pathspec;
                if (read_cache() < 0)
                        die("index file corrupt");
-               add_files_to_cache(verbose, prefix, argv);
+               pathspec = get_pathspec(prefix, argv);
+               add_files_to_cache(verbose, prefix, pathspec);
                goto finish;
        }
 
diff --git a/builtin-commit.c b/builtin-commit.c
new file mode 100644 (file)
index 0000000..5d27102
--- /dev/null
@@ -0,0 +1,814 @@
+/*
+ * Builtin "git commit"
+ *
+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
+ * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
+ */
+
+#include "cache.h"
+#include "cache-tree.h"
+#include "dir.h"
+#include "builtin.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "commit.h"
+#include "revision.h"
+#include "wt-status.h"
+#include "run-command.h"
+#include "refs.h"
+#include "log-tree.h"
+#include "strbuf.h"
+#include "utf8.h"
+#include "parse-options.h"
+#include "path-list.h"
+
+static const char * const builtin_commit_usage[] = {
+       "git-commit [options] [--] <filepattern>...",
+       NULL
+};
+
+static unsigned char head_sha1[20], merge_head_sha1[20];
+static char *use_message_buffer;
+static const char commit_editmsg[] = "COMMIT_EDITMSG";
+static struct lock_file index_lock; /* real index */
+static struct lock_file false_lock; /* used only for partial commits */
+static enum {
+       COMMIT_AS_IS = 1,
+       COMMIT_NORMAL,
+       COMMIT_PARTIAL,
+} commit_style;
+
+static char *logfile, *force_author, *template_file;
+static char *edit_message, *use_message;
+static int all, edit_flag, also, interactive, only, amend, signoff;
+static int quiet, verbose, untracked_files, no_verify;
+
+static int no_edit, initial_commit, in_merge;
+const char *only_include_assumed;
+struct strbuf message;
+
+static int opt_parse_m(const struct option *opt, const char *arg, int unset)
+{
+       struct strbuf *buf = opt->value;
+       if (unset)
+               strbuf_setlen(buf, 0);
+       else {
+               strbuf_addstr(buf, arg);
+               strbuf_addch(buf, '\n');
+               strbuf_addch(buf, '\n');
+       }
+       return 0;
+}
+
+static struct option builtin_commit_options[] = {
+       OPT__QUIET(&quiet),
+       OPT__VERBOSE(&verbose),
+       OPT_GROUP("Commit message options"),
+
+       OPT_STRING('F', "file", &logfile, "FILE", "read log from file"),
+       OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
+       OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
+       OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "),
+       OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
+       OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by: header"),
+       OPT_STRING('t', "template", &template_file, "FILE", "use specified template file"),
+       OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
+
+       OPT_GROUP("Commit contents options"),
+       OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
+       OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
+       OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
+       OPT_BOOLEAN('o', "only", &only, ""),
+       OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
+       OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
+       OPT_BOOLEAN(0, "untracked-files", &untracked_files, "show all untracked files"),
+
+       OPT_END()
+};
+
+static void rollback_index_files(void)
+{
+       switch (commit_style) {
+       case COMMIT_AS_IS:
+               break; /* nothing to do */
+       case COMMIT_NORMAL:
+               rollback_lock_file(&index_lock);
+               break;
+       case COMMIT_PARTIAL:
+               rollback_lock_file(&index_lock);
+               rollback_lock_file(&false_lock);
+               break;
+       }
+}
+
+static void commit_index_files(void)
+{
+       switch (commit_style) {
+       case COMMIT_AS_IS:
+               break; /* nothing to do */
+       case COMMIT_NORMAL:
+               commit_lock_file(&index_lock);
+               break;
+       case COMMIT_PARTIAL:
+               commit_lock_file(&index_lock);
+               rollback_lock_file(&false_lock);
+               break;
+       }
+}
+
+/*
+ * Take a union of paths in the index and the named tree (typically, "HEAD"),
+ * and return the paths that match the given pattern in list.
+ */
+static int list_paths(struct path_list *list, const char *with_tree,
+                     const char *prefix, const char **pattern)
+{
+       int i;
+       char *m;
+
+       for (i = 0; pattern[i]; i++)
+               ;
+       m = xcalloc(1, i);
+
+       if (with_tree)
+               overlay_tree_on_cache(with_tree, prefix);
+
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+               if (ce->ce_flags & htons(CE_UPDATE))
+                       continue;
+               if (!pathspec_match(pattern, m, ce->name, 0))
+                       continue;
+               path_list_insert(ce->name, list);
+       }
+
+       return report_path_error(m, pattern, prefix ? strlen(prefix) : 0);
+}
+
+static void add_remove_files(struct path_list *list)
+{
+       int i;
+       for (i = 0; i < list->nr; i++) {
+               struct path_list_item *p = &(list->items[i]);
+               if (file_exists(p->path))
+                       add_file_to_cache(p->path, 0);
+               else
+                       remove_file_from_cache(p->path);
+       }
+}
+
+static char *prepare_index(int argc, const char **argv, const char *prefix)
+{
+       int fd;
+       struct tree *tree;
+       struct path_list partial;
+       const char **pathspec = NULL;
+
+       if (interactive) {
+               interactive_add(argc, argv);
+               commit_style = COMMIT_AS_IS;
+               return get_index_file();
+       }
+
+       if (read_cache() < 0)
+               die("index file corrupt");
+
+       if (*argv)
+               pathspec = get_pathspec(prefix, argv);
+
+       /*
+        * Non partial, non as-is commit.
+        *
+        * (1) get the real index;
+        * (2) update the_index as necessary;
+        * (3) write the_index out to the real index (still locked);
+        * (4) return the name of the locked index file.
+        *
+        * The caller should run hooks on the locked real index, and
+        * (A) if all goes well, commit the real index;
+        * (B) on failure, rollback the real index.
+        */
+       if (all || (also && pathspec && *pathspec)) {
+               int fd = hold_locked_index(&index_lock, 1);
+               add_files_to_cache(0, also ? prefix : NULL, pathspec);
+               refresh_cache(REFRESH_QUIET);
+               if (write_cache(fd, active_cache, active_nr) || close(fd))
+                       die("unable to write new_index file");
+               commit_style = COMMIT_NORMAL;
+               return index_lock.filename;
+       }
+
+       /*
+        * As-is commit.
+        *
+        * (1) return the name of the real index file.
+        *
+        * The caller should run hooks on the real index, and run
+        * hooks on the real index, and create commit from the_index.
+        * We still need to refresh the index here.
+        */
+       if (!pathspec || !*pathspec) {
+               fd = hold_locked_index(&index_lock, 1);
+               refresh_cache(REFRESH_QUIET);
+               if (write_cache(fd, active_cache, active_nr) ||
+                   close(fd) || commit_locked_index(&index_lock))
+                       die("unable to write new_index file");
+               commit_style = COMMIT_AS_IS;
+               return get_index_file();
+       }
+
+       /*
+        * A partial commit.
+        *
+        * (0) find the set of affected paths;
+        * (1) get lock on the real index file;
+        * (2) update the_index with the given paths;
+        * (3) write the_index out to the real index (still locked);
+        * (4) get lock on the false index file;
+        * (5) reset the_index from HEAD;
+        * (6) update the_index the same way as (2);
+        * (7) write the_index out to the false index file;
+        * (8) return the name of the false index file (still locked);
+        *
+        * The caller should run hooks on the locked false index, and
+        * create commit from it.  Then
+        * (A) if all goes well, commit the real index;
+        * (B) on failure, rollback the real index;
+        * In either case, rollback the false index.
+        */
+       commit_style = COMMIT_PARTIAL;
+
+       if (file_exists(git_path("MERGE_HEAD")))
+               die("cannot do a partial commit during a merge.");
+
+       memset(&partial, 0, sizeof(partial));
+       partial.strdup_paths = 1;
+       if (list_paths(&partial, initial_commit ? NULL : "HEAD", prefix, pathspec))
+               exit(1);
+
+       discard_cache();
+       if (read_cache() < 0)
+               die("cannot read the index");
+
+       fd = hold_locked_index(&index_lock, 1);
+       add_remove_files(&partial);
+       refresh_cache(REFRESH_QUIET);
+       if (write_cache(fd, active_cache, active_nr) || close(fd))
+               die("unable to write new_index file");
+
+       fd = hold_lock_file_for_update(&false_lock,
+                                      git_path("next-index-%d", getpid()), 1);
+       discard_cache();
+       if (!initial_commit) {
+               tree = parse_tree_indirect(head_sha1);
+               if (!tree)
+                       die("failed to unpack HEAD tree object");
+               if (read_tree(tree, 0, NULL))
+                       die("failed to read HEAD tree object");
+       }
+       add_remove_files(&partial);
+       refresh_cache(REFRESH_QUIET);
+
+       if (write_cache(fd, active_cache, active_nr) || close(fd))
+               die("unable to write temporary index file");
+       return false_lock.filename;
+}
+
+static int run_status(FILE *fp, const char *index_file, const char *prefix)
+{
+       struct wt_status s;
+
+       wt_status_prepare(&s);
+       s.prefix = prefix;
+
+       if (amend) {
+               s.amend = 1;
+               s.reference = "HEAD^1";
+       }
+       s.verbose = verbose;
+       s.untracked = untracked_files;
+       s.index_file = index_file;
+       s.fp = fp;
+
+       wt_status_print(&s);
+
+       return s.commitable;
+}
+
+static const char sign_off_header[] = "Signed-off-by: ";
+
+static int prepare_log_message(const char *index_file, const char *prefix)
+{
+       struct stat statbuf;
+       int commitable, saved_color_setting;
+       struct strbuf sb;
+       char *buffer;
+       FILE *fp;
+
+       strbuf_init(&sb, 0);
+       if (message.len) {
+               strbuf_addbuf(&sb, &message);
+       } else if (logfile && !strcmp(logfile, "-")) {
+               if (isatty(0))
+                       fprintf(stderr, "(reading log message from standard input)\n");
+               if (strbuf_read(&sb, 0, 0) < 0)
+                       die("could not read log from standard input");
+       } else if (logfile) {
+               if (strbuf_read_file(&sb, logfile, 0) < 0)
+                       die("could not read log file '%s': %s",
+                           logfile, strerror(errno));
+       } else if (use_message) {
+               buffer = strstr(use_message_buffer, "\n\n");
+               if (!buffer || buffer[2] == '\0')
+                       die("commit has empty message");
+               strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
+       } else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
+               if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
+                       die("could not read MERGE_MSG: %s", strerror(errno));
+       } else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
+               if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
+                       die("could not read SQUASH_MSG: %s", strerror(errno));
+       } else if (template_file && !stat(template_file, &statbuf)) {
+               if (strbuf_read_file(&sb, template_file, 0) < 0)
+                       die("could not read %s: %s",
+                           template_file, strerror(errno));
+       }
+
+       fp = fopen(git_path(commit_editmsg), "w");
+       if (fp == NULL)
+               die("could not open %s\n", git_path(commit_editmsg));
+
+       stripspace(&sb, 0);
+
+       if (signoff) {
+               struct strbuf sob;
+               int i;
+
+               strbuf_init(&sob, 0);
+               strbuf_addstr(&sob, sign_off_header);
+               strbuf_addstr(&sob, fmt_ident(getenv("GIT_COMMITTER_NAME"),
+                                             getenv("GIT_COMMITTER_EMAIL"),
+                                             "", 1));
+               strbuf_addch(&sob, '\n');
+
+               for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--)
+                       ; /* do nothing */
+               if (prefixcmp(sb.buf + i, sob.buf)) {
+                       if (prefixcmp(sb.buf + i, sign_off_header))
+                               strbuf_addch(&sb, '\n');
+                       strbuf_addbuf(&sb, &sob);
+               }
+               strbuf_release(&sob);
+       }
+
+       if (fwrite(sb.buf, 1, sb.len, fp) < sb.len)
+               die("could not write commit template: %s\n",
+                   strerror(errno));
+
+       strbuf_release(&sb);
+
+       if (in_merge && !no_edit)
+               fprintf(fp,
+                       "#\n"
+                       "# It looks like you may be committing a MERGE.\n"
+                       "# If this is not correct, please remove the file\n"
+                       "#      %s\n"
+                       "# and try again.\n"
+                       "#\n",
+                       git_path("MERGE_HEAD"));
+
+       fprintf(fp,
+               "\n"
+               "# Please enter the commit message for your changes.\n"
+               "# (Comment lines starting with '#' will not be included)\n");
+       if (only_include_assumed)
+               fprintf(fp, "# %s\n", only_include_assumed);
+
+       saved_color_setting = wt_status_use_color;
+       wt_status_use_color = 0;
+       commitable = run_status(fp, index_file, prefix);
+       wt_status_use_color = saved_color_setting;
+
+       fclose(fp);
+
+       return commitable;
+}
+
+/*
+ * Find out if the message starting at position 'start' in the strbuf
+ * contains only whitespace and Signed-off-by lines.
+ */
+static int message_is_empty(struct strbuf *sb, int start)
+{
+       struct strbuf tmpl;
+       const char *nl;
+       int eol, i;
+
+       /* See if the template is just a prefix of the message. */
+       strbuf_init(&tmpl, 0);
+       if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) {
+               stripspace(&tmpl, 1);
+               if (start + tmpl.len <= sb->len &&
+                   memcmp(tmpl.buf, sb->buf + start, tmpl.len) == 0)
+                       start += tmpl.len;
+       }
+       strbuf_release(&tmpl);
+
+       /* Check if the rest is just whitespace and Signed-of-by's. */
+       for (i = start; i < sb->len; i++) {
+               nl = memchr(sb->buf + i, '\n', sb->len - i);
+               if (nl)
+                       eol = nl - sb->buf;
+               else
+                       eol = sb->len;
+
+               if (strlen(sign_off_header) <= eol - i &&
+                   !prefixcmp(sb->buf + i, sign_off_header)) {
+                       i = eol;
+                       continue;
+               }
+               while (i < eol)
+                       if (!isspace(sb->buf[i++]))
+                               return 0;
+       }
+
+       return 1;
+}
+
+static void determine_author_info(struct strbuf *sb)
+{
+       char *name, *email, *date;
+
+       name = getenv("GIT_AUTHOR_NAME");
+       email = getenv("GIT_AUTHOR_EMAIL");
+       date = getenv("GIT_AUTHOR_DATE");
+
+       if (use_message) {
+               const char *a, *lb, *rb, *eol;
+
+               a = strstr(use_message_buffer, "\nauthor ");
+               if (!a)
+                       die("invalid commit: %s\n", use_message);
+
+               lb = strstr(a + 8, " <");
+               rb = strstr(a + 8, "> ");
+               eol = strchr(a + 8, '\n');
+               if (!lb || !rb || !eol)
+                       die("invalid commit: %s\n", use_message);
+
+               name = xstrndup(a + 8, lb - (a + 8));
+               email = xstrndup(lb + 2, rb - (lb + 2));
+               date = xstrndup(rb + 2, eol - (rb + 2));
+       }
+
+       if (force_author) {
+               const char *lb = strstr(force_author, " <");
+               const char *rb = strchr(force_author, '>');
+
+               if (!lb || !rb)
+                       die("malformed --author parameter\n");
+               name = xstrndup(force_author, lb - force_author);
+               email = xstrndup(lb + 2, rb - (lb + 2));
+       }
+
+       strbuf_addf(sb, "author %s\n", fmt_ident(name, email, date, 1));
+}
+
+static int parse_and_validate_options(int argc, const char *argv[])
+{
+       int f = 0;
+
+       argc = parse_options(argc, argv, builtin_commit_options,
+                            builtin_commit_usage, 0);
+
+       if (logfile || message.len || use_message)
+               no_edit = 1;
+       if (edit_flag)
+               no_edit = 0;
+
+       if (get_sha1("HEAD", head_sha1))
+               initial_commit = 1;
+
+       if (!get_sha1("MERGE_HEAD", merge_head_sha1))
+               in_merge = 1;
+
+       /* Sanity check options */
+       if (amend && initial_commit)
+               die("You have nothing to amend.");
+       if (amend && in_merge)
+               die("You are in the middle of a merger -- cannot amend.");
+
+       if (use_message)
+               f++;
+       if (edit_message)
+               f++;
+       if (logfile)
+               f++;
+       if (f > 1)
+               die("Only one of -c/-C/-F can be used.");
+       if (message.len && f > 0)
+               die("Option -m cannot be combined with -c/-C/-F.");
+       if (edit_message)
+               use_message = edit_message;
+       if (amend)
+               use_message = "HEAD";
+       if (use_message) {
+               unsigned char sha1[20];
+               static char utf8[] = "UTF-8";
+               const char *out_enc;
+               char *enc, *end;
+               struct commit *commit;
+
+               if (get_sha1(use_message, sha1))
+                       die("could not lookup commit %s", use_message);
+               commit = lookup_commit(sha1);
+               if (!commit || parse_commit(commit))
+                       die("could not parse commit %s", use_message);
+
+               enc = strstr(commit->buffer, "\nencoding");
+               if (enc) {
+                       end = strchr(enc + 10, '\n');
+                       enc = xstrndup(enc + 10, end - (enc + 10));
+               } else {
+                       enc = utf8;
+               }
+               out_enc = git_commit_encoding ? git_commit_encoding : utf8;
+
+               if (strcmp(out_enc, enc))
+                       use_message_buffer =
+                               reencode_string(commit->buffer, out_enc, enc);
+
+               /*
+                * If we failed to reencode the buffer, just copy it
+                * byte for byte so the user can try to fix it up.
+                * This also handles the case where input and output
+                * encodings are identical.
+                */
+               if (use_message_buffer == NULL)
+                       use_message_buffer = xstrdup(commit->buffer);
+               if (enc != utf8)
+                       free(enc);
+       }
+
+       if (!!also + !!only + !!all + !!interactive > 1)
+               die("Only one of --include/--only/--all/--interactive can be used.");
+       if (argc == 0 && (also || (only && !amend)))
+               die("No paths with --include/--only does not make sense.");
+       if (argc == 0 && only && amend)
+               only_include_assumed = "Clever... amending the last one with dirty index.";
+       if (argc > 0 && !also && !only) {
+               only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths...";
+               also = 0;
+       }
+
+       if (all && argc > 0)
+               die("Paths with -a does not make sense.");
+       else if (interactive && argc > 0)
+               die("Paths with --interactive does not make sense.");
+
+       return argc;
+}
+
+int cmd_status(int argc, const char **argv, const char *prefix)
+{
+       const char *index_file;
+       int commitable;
+
+       git_config(git_status_config);
+
+       argc = parse_and_validate_options(argc, argv);
+
+       index_file = prepare_index(argc, argv, prefix);
+
+       commitable = run_status(stdout, index_file, prefix);
+
+       rollback_index_files();
+
+       return commitable ? 0 : 1;
+}
+
+static int run_hook(const char *index_file, const char *name, const char *arg)
+{
+       struct child_process hook;
+       const char *argv[3], *env[2];
+       char index[PATH_MAX];
+
+       argv[0] = git_path("hooks/%s", name);
+       argv[1] = arg;
+       argv[2] = NULL;
+       snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
+       env[0] = index;
+       env[1] = NULL;
+
+       if (access(argv[0], X_OK) < 0)
+               return 0;
+
+       memset(&hook, 0, sizeof(hook));
+       hook.argv = argv;
+       hook.no_stdin = 1;
+       hook.stdout_to_stderr = 1;
+       hook.env = env;
+
+       return run_command(&hook);
+}
+
+static void print_summary(const char *prefix, const unsigned char *sha1)
+{
+       struct rev_info rev;
+       struct commit *commit;
+
+       commit = lookup_commit(sha1);
+       if (!commit)
+               die("couldn't look up newly created commit\n");
+       if (!commit || parse_commit(commit))
+               die("could not parse newly created commit");
+
+       init_revisions(&rev, prefix);
+       setup_revisions(0, NULL, &rev, NULL);
+
+       rev.abbrev = 0;
+       rev.diff = 1;
+       rev.diffopt.output_format =
+               DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
+
+       rev.verbose_header = 1;
+       rev.show_root_diff = 1;
+       rev.commit_format = get_commit_format("format:%h: %s");
+       rev.always_show_header = 1;
+
+       printf("Created %scommit ", initial_commit ? "initial " : "");
+
+       log_tree_commit(&rev, commit);
+       printf("\n");
+}
+
+int git_commit_config(const char *k, const char *v)
+{
+       if (!strcmp(k, "commit.template")) {
+               template_file = xstrdup(v);
+               return 0;
+       }
+
+       return git_status_config(k, v);
+}
+
+static const char commit_utf8_warn[] =
+"Warning: commit message does not conform to UTF-8.\n"
+"You may want to amend it after fixing the message, or set the config\n"
+"variable i18n.commitencoding to the encoding your project uses.\n";
+
+int cmd_commit(int argc, const char **argv, const char *prefix)
+{
+       int header_len;
+       struct strbuf sb;
+       const char *index_file, *reflog_msg;
+       char *nl, *p;
+       unsigned char commit_sha1[20];
+       struct ref_lock *ref_lock;
+
+       git_config(git_commit_config);
+
+       argc = parse_and_validate_options(argc, argv);
+
+       index_file = prepare_index(argc, argv, prefix);
+
+       if (!no_verify && run_hook(index_file, "pre-commit", NULL)) {
+               rollback_index_files();
+               return 1;
+       }
+
+       if (!prepare_log_message(index_file, prefix) && !in_merge) {
+               run_status(stdout, index_file, prefix);
+               rollback_index_files();
+               unlink(commit_editmsg);
+               return 1;
+       }
+
+       /*
+        * Re-read the index as pre-commit hook could have updated it,
+        * and write it out as a tree.
+        */
+       discard_cache();
+       read_cache_from(index_file);
+       if (!active_cache_tree)
+               active_cache_tree = cache_tree();
+       if (cache_tree_update(active_cache_tree,
+                             active_cache, active_nr, 0, 0) < 0) {
+               rollback_index_files();
+               die("Error building trees");
+       }
+
+       /*
+        * The commit object
+        */
+       strbuf_init(&sb, 0);
+       strbuf_addf(&sb, "tree %s\n",
+                   sha1_to_hex(active_cache_tree->sha1));
+
+       /* Determine parents */
+       if (initial_commit) {
+               reflog_msg = "commit (initial)";
+       } else if (amend) {
+               struct commit_list *c;
+               struct commit *commit;
+
+               reflog_msg = "commit (amend)";
+               commit = lookup_commit(head_sha1);
+               if (!commit || parse_commit(commit))
+                       die("could not parse HEAD commit");
+
+               for (c = commit->parents; c; c = c->next)
+                       strbuf_addf(&sb, "parent %s\n",
+                                     sha1_to_hex(c->item->object.sha1));
+       } else if (in_merge) {
+               struct strbuf m;
+               FILE *fp;
+
+               reflog_msg = "commit (merge)";
+               strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
+               strbuf_init(&m, 0);
+               fp = fopen(git_path("MERGE_HEAD"), "r");
+               if (fp == NULL)
+                       die("could not open %s for reading: %s",
+                           git_path("MERGE_HEAD"), strerror(errno));
+               while (strbuf_getline(&m, fp, '\n') != EOF)
+                       strbuf_addf(&sb, "parent %s\n", m.buf);
+               fclose(fp);
+               strbuf_release(&m);
+       } else {
+               reflog_msg = "commit";
+               strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
+       }
+
+       determine_author_info(&sb);
+       strbuf_addf(&sb, "committer %s\n", git_committer_info(1));
+       if (!is_encoding_utf8(git_commit_encoding))
+               strbuf_addf(&sb, "encoding %s\n", git_commit_encoding);
+       strbuf_addch(&sb, '\n');
+
+       /* Get the commit message and validate it */
+       header_len = sb.len;
+       if (!no_edit)
+               launch_editor(git_path(commit_editmsg), &sb);
+       else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
+               rollback_index_files();
+               die("could not read commit message\n");
+       }
+       if (run_hook(index_file, "commit-msg", git_path(commit_editmsg))) {
+               rollback_index_files();
+               exit(1);
+       }
+
+       /* Truncate the message just before the diff, if any. */
+       p = strstr(sb.buf, "\ndiff --git a/");
+       if (p != NULL)
+               strbuf_setlen(&sb, p - sb.buf);
+
+       stripspace(&sb, 1);
+       if (sb.len < header_len || message_is_empty(&sb, header_len)) {
+               rollback_index_files();
+               die("* no commit message?  aborting commit.");
+       }
+       strbuf_addch(&sb, '\0');
+       if (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf))
+               fprintf(stderr, commit_utf8_warn);
+
+       if (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1)) {
+               rollback_index_files();
+               die("failed to write commit object");
+       }
+
+       ref_lock = lock_any_ref_for_update("HEAD",
+                                          initial_commit ? NULL : head_sha1,
+                                          0);
+
+       nl = strchr(sb.buf + header_len, '\n');
+       if (nl)
+               strbuf_setlen(&sb, nl + 1 - sb.buf);
+       else
+               strbuf_addch(&sb, '\n');
+       strbuf_remove(&sb, 0, header_len);
+       strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
+       strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
+
+       if (!ref_lock) {
+               rollback_index_files();
+               die("cannot lock HEAD ref");
+       }
+       if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) {
+               rollback_index_files();
+               die("cannot update HEAD ref");
+       }
+
+       unlink(git_path("MERGE_HEAD"));
+       unlink(git_path("MERGE_MSG"));
+
+       commit_index_files();
+
+       rerere();
+       run_hook(get_index_file(), "post-commit", NULL);
+       if (!quiet)
+               print_summary(prefix, commit_sha1);
+
+       return 0;
+}
index 7f607098305fcf14c3e2a7634b6a5dc118445306..0f0ab2da167c57402efad9dc09e58964759c23cd 100644 (file)
@@ -38,28 +38,28 @@ static const char *tag_modified = "";
 
 
 /*
- * Match a pathspec against a filename. The first "len" characters
+ * Match a pathspec against a filename. The first "skiplen" characters
  * are the common prefix
  */
-static int match(const char **spec, char *ps_matched,
-                const char *filename, int len)
+int pathspec_match(const char **spec, char *ps_matched,
+                  const char *filename, int skiplen)
 {
        const char *m;
 
        while ((m = *spec++) != NULL) {
-               int matchlen = strlen(m + len);
+               int matchlen = strlen(m + skiplen);
 
                if (!matchlen)
                        goto matched;
-               if (!strncmp(m + len, filename + len, matchlen)) {
-                       if (m[len + matchlen - 1] == '/')
+               if (!strncmp(m + skiplen, filename + skiplen, matchlen)) {
+                       if (m[skiplen + matchlen - 1] == '/')
                                goto matched;
-                       switch (filename[len + matchlen]) {
+                       switch (filename[skiplen + matchlen]) {
                        case '/': case '\0':
                                goto matched;
                        }
                }
-               if (!fnmatch(m + len, filename + len, 0))
+               if (!fnmatch(m + skiplen, filename + skiplen, 0))
                        goto matched;
                if (ps_matched)
                        ps_matched++;
@@ -80,7 +80,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
        if (len >= ent->len)
                die("git-ls-files: internal error - directory entry not superset of prefix");
 
-       if (pathspec && !match(pathspec, ps_matched, ent->name, len))
+       if (pathspec && !pathspec_match(pathspec, ps_matched, ent->name, len))
                return;
 
        fputs(tag, stdout);
@@ -185,7 +185,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
        if (len >= ce_namelen(ce))
                die("git-ls-files: internal error - cache entry not superset of prefix");
 
-       if (pathspec && !match(pathspec, ps_matched, ce->name, len))
+       if (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, len))
                return;
 
        if (tag && *tag && show_valid_bit &&
@@ -331,7 +331,7 @@ static const char *verify_pathspec(const char *prefix)
  * that were given from the command line.  We are not
  * going to write this index out.
  */
-static void overlay_tree(const char *tree_name, const char *prefix)
+void overlay_tree_on_cache(const char *tree_name, const char *prefix)
 {
        struct tree *tree;
        unsigned char sha1[20];
@@ -384,6 +384,42 @@ static void overlay_tree(const char *tree_name, const char *prefix)
        }
 }
 
+int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
+{
+       /*
+        * Make sure all pathspec matched; otherwise it is an error.
+        */
+       int num, errors = 0;
+       for (num = 0; pathspec[num]; num++) {
+               int other, found_dup;
+
+               if (ps_matched[num])
+                       continue;
+               /*
+                * The caller might have fed identical pathspec
+                * twice.  Do not barf on such a mistake.
+                */
+               for (found_dup = other = 0;
+                    !found_dup && pathspec[other];
+                    other++) {
+                       if (other == num || !ps_matched[other])
+                               continue;
+                       if (!strcmp(pathspec[other], pathspec[num]))
+                               /*
+                                * Ok, we have a match already.
+                                */
+                               found_dup = 1;
+               }
+               if (found_dup)
+                       continue;
+
+               error("pathspec '%s' did not match any file(s) known to git.",
+                     pathspec[num] + prefix_offset);
+               errors++;
+       }
+       return errors;
+}
+
 static const char ls_files_usage[] =
        "git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
        "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
@@ -568,47 +604,17 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
                 */
                if (show_stage || show_unmerged)
                        die("ls-files --with-tree is incompatible with -s or -u");
-               overlay_tree(with_tree, prefix);
+               overlay_tree_on_cache(with_tree, prefix);
        }
        show_files(&dir, prefix);
 
        if (ps_matched) {
-               /* We need to make sure all pathspec matched otherwise
-                * it is an error.
-                */
-               int num, errors = 0;
-               for (num = 0; pathspec[num]; num++) {
-                       int other, found_dup;
-
-                       if (ps_matched[num])
-                               continue;
-                       /*
-                        * The caller might have fed identical pathspec
-                        * twice.  Do not barf on such a mistake.
-                        */
-                       for (found_dup = other = 0;
-                            !found_dup && pathspec[other];
-                            other++) {
-                               if (other == num || !ps_matched[other])
-                                       continue;
-                               if (!strcmp(pathspec[other], pathspec[num]))
-                                       /*
-                                        * Ok, we have a match already.
-                                        */
-                                       found_dup = 1;
-                       }
-                       if (found_dup)
-                               continue;
-
-                       error("pathspec '%s' did not match any file(s) known to git.",
-                             pathspec[num] + prefix_offset);
-                       errors++;
-               }
-
-               if (errors)
+               int bad;
+               bad = report_path_error(ps_matched, pathspec, prefix_offset);
+               if (bad)
                        fprintf(stderr, "Did you forget to 'git add'?\n");
 
-               return errors ? 1 : 0;
+               return bad ? 1 : 0;
        }
 
        return 0;
diff --git a/builtin-merge-ours.c b/builtin-merge-ours.c
new file mode 100644 (file)
index 0000000..8f5bbaf
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Implementation of git-merge-ours.sh as builtin
+ *
+ * Copyright (c) 2007 Thomas Harning Jr
+ * Original:
+ * Original Copyright (c) 2005 Junio C Hamano
+ *
+ * Pretend we resolved the heads, but declare our tree trumps everybody else.
+ */
+#include "git-compat-util.h"
+#include "builtin.h"
+
+static const char *diff_index_args[] = {
+       "diff-index", "--quiet", "--cached", "HEAD", "--", NULL
+};
+#define NARGS (ARRAY_SIZE(diff_index_args) - 1)
+
+int cmd_merge_ours(int argc, const char **argv, const char *prefix)
+{
+       /*
+        * We need to exit with 2 if the index does not match our HEAD tree,
+        * because the current index is what we will be committing as the
+        * merge result.
+        */
+       if (cmd_diff_index(NARGS, diff_index_args, prefix))
+               exit(2);
+       exit(0);
+}
index 2db25c88bf648e4d5f2f41502fd29c5f7324e56b..8d167a9674e70a36662528c98165bd3be8a9f431 100644 (file)
@@ -14,6 +14,7 @@ int cmd_runstatus(int argc, const char **argv, const char *prefix)
 
        git_config(git_status_config);
        wt_status_prepare(&s);
+       s.prefix = prefix;
 
        for (i = 1; i < argc; i++) {
                if (!strcmp(argv[i], "--color"))
index cbb0f04e85627a5509bb2ba8cbf04a54d4d20c41..566b9d186fb8a20841cce23914643556c6c24382 100644 (file)
@@ -17,11 +17,9 @@ static const char builtin_tag_usage[] =
 
 static char signingkey[1000];
 
-static void launch_editor(const char *path, struct strbuf *buffer)
+void launch_editor(const char *path, struct strbuf *buffer)
 {
        const char *editor, *terminal;
-       struct child_process child;
-       const char *args[3];
 
        editor = getenv("GIT_EDITOR");
        if (!editor && editor_program)
@@ -42,14 +40,12 @@ static void launch_editor(const char *path, struct strbuf *buffer)
        if (!editor)
                editor = "vi";
 
-       memset(&child, 0, sizeof(child));
-       child.argv = args;
-       args[0] = editor;
-       args[1] = path;
-       args[2] = NULL;
+       if (strcmp(editor, ":")) {
+               const char *args[] = { editor, path, NULL };
 
-       if (run_command(&child))
-               die("There was a problem with the editor %s.", editor);
+               if (run_command_v_opt(args, 0))
+                       die("There was a problem with the editor %s.", editor);
+       }
 
        if (strbuf_read_file(buffer, path, 0) < 0)
                die("could not read message file '%s': %s",
index 9a6213af126212dc131133cc2e9bf8b8d7a34557..caea1a94e40f2e103b75694fa14d17f628d29935 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -24,6 +24,7 @@ extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
 extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
 extern int cmd_cherry(int argc, const char **argv, const char *prefix);
 extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
+extern int cmd_commit(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
 extern int cmd_describe(int argc, const char **argv, const char *prefix);
@@ -51,6 +52,7 @@ extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
+extern int cmd_merge_ours(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
 extern int cmd_mv(int argc, const char **argv, const char *prefix);
 extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
@@ -68,10 +70,10 @@ extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 extern int cmd_revert(int argc, const char **argv, const char *prefix);
 extern int cmd_rm(int argc, const char **argv, const char *prefix);
-extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
 extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
 extern int cmd_show(int argc, const char **argv, const char *prefix);
 extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
+extern int cmd_status(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
 extern int cmd_tag(int argc, const char **argv, const char *prefix);
index e4d60cde6f1b9dd7a2aab36ebc4f2a97a9d61b22..9b9b9166df05e984dc571462f333aeaee9cdcd23 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -6,6 +6,7 @@
 #include "revision.h"
 #include "list-objects.h"
 #include "run-command.h"
+#include "refs.h"
 
 static const char bundle_signature[] = "# v2 git bundle\n";
 
@@ -232,11 +233,17 @@ int create_bundle(struct bundle_header *header, const char *path,
                struct object_array_entry *e = revs.pending.objects + i;
                unsigned char sha1[20];
                char *ref;
+               const char *display_ref;
+               int flag;
 
                if (e->item->flags & UNINTERESTING)
                        continue;
                if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
                        continue;
+               if (!resolve_ref(e->name, sha1, 1, &flag))
+                       flag = 0;
+               display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
+
                /*
                 * Make sure the refs we wrote out is correct; --max-count and
                 * other limiting options could have prevented all the tips
@@ -287,7 +294,7 @@ int create_bundle(struct bundle_header *header, const char *path,
                ref_count++;
                write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40);
                write_or_die(bundle_fd, " ", 1);
-               write_or_die(bundle_fd, ref, strlen(ref));
+               write_or_die(bundle_fd, display_ref, strlen(display_ref));
                write_or_die(bundle_fd, "\n", 1);
                free(ref);
        }
diff --git a/cache.h b/cache.h
index 33ebccf48d0d8963ab5b262bfb87312e6542ed75..cf0bdc674cc81f0443be3776b01969e8e07d2025 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -604,10 +604,18 @@ extern void trace_argv_printf(const char **argv, int count, const char *format,
 extern int convert_to_git(const char *path, const char *src, size_t len, struct strbuf *dst);
 extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
 
+/* add */
+void add_files_to_cache(int verbose, const char *prefix, const char **pathspec);
+
 /* diff.c */
 extern int diff_auto_refresh_index;
 
 /* match-trees.c */
 void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
 
+/* ls-files */
+int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
+int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
+void overlay_tree_on_cache(const char *tree_name, const char *prefix);
+
 #endif /* CACHE_H */
index d82b8bc3007d09b6a33b20e769d52dcc853cd294..9f0765bd9c30672dc93b4c3fc0eff816d1b3729b 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -114,7 +114,6 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,
 int in_merge_bases(struct commit *, struct commit **, int);
 
 extern int interactive_add(int argc, const char **argv);
-extern void add_files_to_cache(int verbose, const char *prefix, const char **files);
 extern int rerere(void);
 
 static inline int single_parent(struct commit *commit)
diff --git a/contrib/examples/git-commit.sh b/contrib/examples/git-commit.sh
new file mode 100755 (executable)
index 0000000..4853397
--- /dev/null
@@ -0,0 +1,629 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Linus Torvalds
+# Copyright (c) 2006 Junio C Hamano
+
+USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]'
+SUBDIRECTORY_OK=Yes
+OPTIONS_SPEC=
+. git-sh-setup
+require_work_tree
+
+git rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
+
+case "$0" in
+*status)
+       status_only=t
+       ;;
+*commit)
+       status_only=
+       ;;
+esac
+
+refuse_partial () {
+       echo >&2 "$1"
+       echo >&2 "You might have meant to say 'git commit -i paths...', perhaps?"
+       exit 1
+}
+
+TMP_INDEX=
+THIS_INDEX="${GIT_INDEX_FILE:-$GIT_DIR/index}"
+NEXT_INDEX="$GIT_DIR/next-index$$"
+rm -f "$NEXT_INDEX"
+save_index () {
+       cp -p "$THIS_INDEX" "$NEXT_INDEX"
+}
+
+run_status () {
+       # If TMP_INDEX is defined, that means we are doing
+       # "--only" partial commit, and that index file is used
+       # to build the tree for the commit.  Otherwise, if
+       # NEXT_INDEX exists, that is the index file used to
+       # make the commit.  Otherwise we are using as-is commit
+       # so the regular index file is what we use to compare.
+       if test '' != "$TMP_INDEX"
+       then
+               GIT_INDEX_FILE="$TMP_INDEX"
+               export GIT_INDEX_FILE
+       elif test -f "$NEXT_INDEX"
+       then
+               GIT_INDEX_FILE="$NEXT_INDEX"
+               export GIT_INDEX_FILE
+       fi
+
+       if test "$status_only" = "t" -o "$use_status_color" = "t"; then
+               color=
+       else
+               color=--nocolor
+       fi
+       git runstatus ${color} \
+               ${verbose:+--verbose} \
+               ${amend:+--amend} \
+               ${untracked_files:+--untracked}
+}
+
+trap '
+       test -z "$TMP_INDEX" || {
+               test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
+       }
+       rm -f "$NEXT_INDEX"
+' 0
+
+################################################################
+# Command line argument parsing and sanity checking
+
+all=
+also=
+interactive=
+only=
+logfile=
+use_commit=
+amend=
+edit_flag=
+no_edit=
+log_given=
+log_message=
+verify=t
+quiet=
+verbose=
+signoff=
+force_author=
+only_include_assumed=
+untracked_files=
+templatefile="`git config commit.template`"
+while test $# != 0
+do
+       case "$1" in
+       -F|--F|-f|--f|--fi|--fil|--file)
+               case "$#" in 1) usage ;; esac
+               shift
+               no_edit=t
+               log_given=t$log_given
+               logfile="$1"
+               ;;
+       -F*|-f*)
+               no_edit=t
+               log_given=t$log_given
+               logfile="${1#-[Ff]}"
+               ;;
+       --F=*|--f=*|--fi=*|--fil=*|--file=*)
+               no_edit=t
+               log_given=t$log_given
+               logfile="${1#*=}"
+               ;;
+       -a|--a|--al|--all)
+               all=t
+               ;;
+       --au=*|--aut=*|--auth=*|--autho=*|--author=*)
+               force_author="${1#*=}"
+               ;;
+       --au|--aut|--auth|--autho|--author)
+               case "$#" in 1) usage ;; esac
+               shift
+               force_author="$1"
+               ;;
+       -e|--e|--ed|--edi|--edit)
+               edit_flag=t
+               ;;
+       -i|--i|--in|--inc|--incl|--inclu|--includ|--include)
+               also=t
+               ;;
+       --int|--inte|--inter|--intera|--interac|--interact|--interacti|\
+       --interactiv|--interactive)
+               interactive=t
+               ;;
+       -o|--o|--on|--onl|--only)
+               only=t
+               ;;
+       -m|--m|--me|--mes|--mess|--messa|--messag|--message)
+               case "$#" in 1) usage ;; esac
+               shift
+               log_given=m$log_given
+               log_message="${log_message:+${log_message}
+
+}$1"
+               no_edit=t
+               ;;
+       -m*)
+               log_given=m$log_given
+               log_message="${log_message:+${log_message}
+
+}${1#-m}"
+               no_edit=t
+               ;;
+       --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
+               log_given=m$log_given
+               log_message="${log_message:+${log_message}
+
+}${1#*=}"
+               no_edit=t
+               ;;
+       -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\
+       --no-verify)
+               verify=
+               ;;
+       --a|--am|--ame|--amen|--amend)
+               amend=t
+               use_commit=HEAD
+               ;;
+       -c)
+               case "$#" in 1) usage ;; esac
+               shift
+               log_given=t$log_given
+               use_commit="$1"
+               no_edit=
+               ;;
+       --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
+       --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
+       --reedit-messag=*|--reedit-message=*)
+               log_given=t$log_given
+               use_commit="${1#*=}"
+               no_edit=
+               ;;
+       --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
+       --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\
+       --reedit-message)
+               case "$#" in 1) usage ;; esac
+               shift
+               log_given=t$log_given
+               use_commit="$1"
+               no_edit=
+               ;;
+       -C)
+               case "$#" in 1) usage ;; esac
+               shift
+               log_given=t$log_given
+               use_commit="$1"
+               no_edit=t
+               ;;
+       --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
+       --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
+       --reuse-message=*)
+               log_given=t$log_given
+               use_commit="${1#*=}"
+               no_edit=t
+               ;;
+       --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
+       --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
+               case "$#" in 1) usage ;; esac
+               shift
+               log_given=t$log_given
+               use_commit="$1"
+               no_edit=t
+               ;;
+       -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
+               signoff=t
+               ;;
+       -t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template)
+               case "$#" in 1) usage ;; esac
+               shift
+               templatefile="$1"
+               no_edit=
+               ;;
+       -q|--q|--qu|--qui|--quie|--quiet)
+               quiet=t
+               ;;
+       -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
+               verbose=t
+               ;;
+       -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\
+       --untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\
+       --untracked-file|--untracked-files)
+               untracked_files=t
+               ;;
+       --)
+               shift
+               break
+               ;;
+       -*)
+               usage
+               ;;
+       *)
+               break
+               ;;
+       esac
+       shift
+done
+case "$edit_flag" in t) no_edit= ;; esac
+
+################################################################
+# Sanity check options
+
+case "$amend,$initial_commit" in
+t,t)
+       die "You do not have anything to amend." ;;
+t,)
+       if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
+               die "You are in the middle of a merge -- cannot amend."
+       fi ;;
+esac
+
+case "$log_given" in
+tt*)
+       die "Only one of -c/-C/-F can be used." ;;
+*tm*|*mt*)
+       die "Option -m cannot be combined with -c/-C/-F." ;;
+esac
+
+case "$#,$also,$only,$amend" in
+*,t,t,*)
+       die "Only one of --include/--only can be used." ;;
+0,t,,* | 0,,t,)
+       die "No paths with --include/--only does not make sense." ;;
+0,,t,t)
+       only_include_assumed="# Clever... amending the last one with dirty index." ;;
+0,,,*)
+       ;;
+*,,,*)
+       only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..."
+       also=
+       ;;
+esac
+unset only
+case "$all,$interactive,$also,$#" in
+*t,*t,*)
+       die "Cannot use -a, --interactive or -i at the same time." ;;
+t,,,[1-9]*)
+       die "Paths with -a does not make sense." ;;
+,t,,[1-9]*)
+       die "Paths with --interactive does not make sense." ;;
+,,t,0)
+       die "No paths with -i does not make sense." ;;
+esac
+
+if test ! -z "$templatefile" -a -z "$log_given"
+then
+       if test ! -f "$templatefile"
+       then
+               die "Commit template file does not exist."
+       fi
+fi
+
+################################################################
+# Prepare index to have a tree to be committed
+
+case "$all,$also" in
+t,)
+       if test ! -f "$THIS_INDEX"
+       then
+               die 'nothing to commit (use "git add file1 file2" to include for commit)'
+       fi
+       save_index &&
+       (
+               cd_to_toplevel &&
+               GIT_INDEX_FILE="$NEXT_INDEX" &&
+               export GIT_INDEX_FILE &&
+               git diff-files --name-only -z |
+               git update-index --remove -z --stdin
+       ) || exit
+       ;;
+,t)
+       save_index &&
+       git ls-files --error-unmatch -- "$@" >/dev/null || exit
+
+       git diff-files --name-only -z -- "$@"  |
+       (
+               cd_to_toplevel &&
+               GIT_INDEX_FILE="$NEXT_INDEX" &&
+               export GIT_INDEX_FILE &&
+               git update-index --remove -z --stdin
+       ) || exit
+       ;;
+,)
+       if test "$interactive" = t; then
+               git add --interactive || exit
+       fi
+       case "$#" in
+       0)
+               ;; # commit as-is
+       *)
+               if test -f "$GIT_DIR/MERGE_HEAD"
+               then
+                       refuse_partial "Cannot do a partial commit during a merge."
+               fi
+
+               TMP_INDEX="$GIT_DIR/tmp-index$$"
+               W=
+               test -z "$initial_commit" && W=--with-tree=HEAD
+               commit_only=`git ls-files --error-unmatch $W -- "$@"` || exit
+
+               # Build a temporary index and update the real index
+               # the same way.
+               if test -z "$initial_commit"
+               then
+                       GIT_INDEX_FILE="$THIS_INDEX" \
+                       git read-tree --index-output="$TMP_INDEX" -i -m HEAD
+               else
+                       rm -f "$TMP_INDEX"
+               fi || exit
+
+               printf '%s\n' "$commit_only" |
+               GIT_INDEX_FILE="$TMP_INDEX" \
+               git update-index --add --remove --stdin &&
+
+               save_index &&
+               printf '%s\n' "$commit_only" |
+               (
+                       GIT_INDEX_FILE="$NEXT_INDEX"
+                       export GIT_INDEX_FILE
+                       git update-index --add --remove --stdin
+               ) || exit
+               ;;
+       esac
+       ;;
+esac
+
+################################################################
+# If we do as-is commit, the index file will be THIS_INDEX,
+# otherwise NEXT_INDEX after we make this commit.  We leave
+# the index as is if we abort.
+
+if test -f "$NEXT_INDEX"
+then
+       USE_INDEX="$NEXT_INDEX"
+else
+       USE_INDEX="$THIS_INDEX"
+fi
+
+case "$status_only" in
+t)
+       # This will silently fail in a read-only repository, which is
+       # what we want.
+       GIT_INDEX_FILE="$USE_INDEX" git update-index -q --unmerged --refresh
+       run_status
+       exit $?
+       ;;
+'')
+       GIT_INDEX_FILE="$USE_INDEX" git update-index -q --refresh || exit
+       ;;
+esac
+
+################################################################
+# Grab commit message, write out tree and make commit.
+
+if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit
+then
+    GIT_INDEX_FILE="${TMP_INDEX:-${USE_INDEX}}" "$GIT_DIR"/hooks/pre-commit \
+    || exit
+fi
+
+if test "$log_message" != ''
+then
+       printf '%s\n' "$log_message"
+elif test "$logfile" != ""
+then
+       if test "$logfile" = -
+       then
+               test -t 0 &&
+               echo >&2 "(reading log message from standard input)"
+               cat
+       else
+               cat <"$logfile"
+       fi
+elif test "$use_commit" != ""
+then
+       encoding=$(git config i18n.commitencoding || echo UTF-8)
+       git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
+       sed -e '1,/^$/d' -e 's/^    //'
+elif test -f "$GIT_DIR/MERGE_MSG"
+then
+       cat "$GIT_DIR/MERGE_MSG"
+elif test -f "$GIT_DIR/SQUASH_MSG"
+then
+       cat "$GIT_DIR/SQUASH_MSG"
+elif test "$templatefile" != ""
+then
+       cat "$templatefile"
+fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG
+
+case "$signoff" in
+t)
+       sign=$(git-var GIT_COMMITTER_IDENT | sed -e '
+               s/>.*/>/
+               s/^/Signed-off-by: /
+               ')
+       blank_before_signoff=
+       tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
+       grep 'Signed-off-by:' >/dev/null || blank_before_signoff='
+'
+       tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
+       grep "$sign"$ >/dev/null ||
+       printf '%s%s\n' "$blank_before_signoff" "$sign" \
+               >>"$GIT_DIR"/COMMIT_EDITMSG
+       ;;
+esac
+
+if test -f "$GIT_DIR/MERGE_HEAD" && test -z "$no_edit"; then
+       echo "#"
+       echo "# It looks like you may be committing a MERGE."
+       echo "# If this is not correct, please remove the file"
+       printf '%s\n' "#        $GIT_DIR/MERGE_HEAD"
+       echo "# and try again"
+       echo "#"
+fi >>"$GIT_DIR"/COMMIT_EDITMSG
+
+# Author
+if test '' != "$use_commit"
+then
+       eval "$(get_author_ident_from_commit "$use_commit")"
+       export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
+fi
+if test '' != "$force_author"
+then
+       GIT_AUTHOR_NAME=`expr "z$force_author" : 'z\(.*[^ ]\) *<.*'` &&
+       GIT_AUTHOR_EMAIL=`expr "z$force_author" : '.*\(<.*\)'` &&
+       test '' != "$GIT_AUTHOR_NAME" &&
+       test '' != "$GIT_AUTHOR_EMAIL" ||
+       die "malformed --author parameter"
+       export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
+fi
+
+PARENTS="-p HEAD"
+if test -z "$initial_commit"
+then
+       rloga='commit'
+       if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
+               rloga='commit (merge)'
+               PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
+       elif test -n "$amend"; then
+               rloga='commit (amend)'
+               PARENTS=$(git cat-file commit HEAD |
+                       sed -n -e '/^$/q' -e 's/^parent /-p /p')
+       fi
+       current="$(git rev-parse --verify HEAD)"
+else
+       if [ -z "$(git ls-files)" ]; then
+               echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)'
+               exit 1
+       fi
+       PARENTS=""
+       rloga='commit (initial)'
+       current=''
+fi
+set_reflog_action "$rloga"
+
+if test -z "$no_edit"
+then
+       {
+               echo ""
+               echo "# Please enter the commit message for your changes."
+               echo "# (Comment lines starting with '#' will not be included)"
+               test -z "$only_include_assumed" || echo "$only_include_assumed"
+               run_status
+       } >>"$GIT_DIR"/COMMIT_EDITMSG
+else
+       # we need to check if there is anything to commit
+       run_status >/dev/null
+fi
+if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
+then
+       rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
+       use_status_color=t
+       run_status
+       exit 1
+fi
+
+case "$no_edit" in
+'')
+       git-var GIT_AUTHOR_IDENT > /dev/null  || die
+       git-var GIT_COMMITTER_IDENT > /dev/null  || die
+       git_editor "$GIT_DIR/COMMIT_EDITMSG"
+       ;;
+esac
+
+case "$verify" in
+t)
+       if test -x "$GIT_DIR"/hooks/commit-msg
+       then
+               "$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit
+       fi
+esac
+
+if test -z "$no_edit"
+then
+    sed -e '
+        /^diff --git a\/.*/{
+           s///
+           q
+       }
+       /^#/d
+    ' "$GIT_DIR"/COMMIT_EDITMSG
+else
+    cat "$GIT_DIR"/COMMIT_EDITMSG
+fi |
+git stripspace >"$GIT_DIR"/COMMIT_MSG
+
+# Test whether the commit message has any content we didn't supply.
+have_commitmsg=
+grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
+       git stripspace > "$GIT_DIR"/COMMIT_BAREMSG
+
+# Is the commit message totally empty?
+if test -s "$GIT_DIR"/COMMIT_BAREMSG
+then
+       if test "$templatefile" != ""
+       then
+               # Test whether this is just the unaltered template.
+               if cnt=`sed -e '/^#/d' < "$templatefile" |
+                       git stripspace |
+                       diff "$GIT_DIR"/COMMIT_BAREMSG - |
+                       wc -l` &&
+                  test 0 -lt $cnt
+               then
+                       have_commitmsg=t
+               fi
+       else
+               # No template, so the content in the commit message must
+               # have come from the user.
+               have_commitmsg=t
+       fi
+fi
+
+rm -f "$GIT_DIR"/COMMIT_BAREMSG
+
+if test "$have_commitmsg" = "t"
+then
+       if test -z "$TMP_INDEX"
+       then
+               tree=$(GIT_INDEX_FILE="$USE_INDEX" git write-tree)
+       else
+               tree=$(GIT_INDEX_FILE="$TMP_INDEX" git write-tree) &&
+               rm -f "$TMP_INDEX"
+       fi &&
+       commit=$(git commit-tree $tree $PARENTS <"$GIT_DIR/COMMIT_MSG") &&
+       rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
+       git update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" &&
+       rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" &&
+       if test -f "$NEXT_INDEX"
+       then
+               mv "$NEXT_INDEX" "$THIS_INDEX"
+       else
+               : ;# happy
+       fi
+else
+       echo >&2 "* no commit message?  aborting commit."
+       false
+fi
+ret="$?"
+rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
+
+cd_to_toplevel
+
+git rerere
+
+if test "$ret" = 0
+then
+       git gc --auto
+       if test -x "$GIT_DIR"/hooks/post-commit
+       then
+               "$GIT_DIR"/hooks/post-commit
+       fi
+       if test -z "$quiet"
+       then
+               commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\
+                      --summary --root HEAD --`
+               echo "Created${initial_commit:+ initial} commit $commit"
+       fi
+fi
+
+exit "$ret"
diff --git a/contrib/examples/git-merge-ours.sh b/contrib/examples/git-merge-ours.sh
new file mode 100755 (executable)
index 0000000..c81a790
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+# Pretend we resolved the heads, but declare our tree trumps everybody else.
+#
+
+# We need to exit with 2 if the index does not match our HEAD tree,
+# because the current index is what we will be committing as the
+# merge result.
+
+git diff-index --quiet --cached HEAD || exit 2
+
+exit 0
diff --git a/dir.c b/dir.c
index 225fdfb52c432b7af1af1cdf9aa9a31f8022e3ec..11a4cf3e16f840ca826b927ce504535eec8c71db 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -690,11 +690,10 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i
        return dir->nr;
 }
 
-int
-file_exists(const char *f)
+int file_exists(const char *f)
 {
-  struct stat sb;
-  return stat(f, &sb) == 0;
+       struct stat sb;
+       return lstat(f, &sb) == 0;
 }
 
 /*
diff --git a/git-commit.sh b/git-commit.sh
deleted file mode 100755 (executable)
index 4853397..0000000
+++ /dev/null
@@ -1,629 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Linus Torvalds
-# Copyright (c) 2006 Junio C Hamano
-
-USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]'
-SUBDIRECTORY_OK=Yes
-OPTIONS_SPEC=
-. git-sh-setup
-require_work_tree
-
-git rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
-
-case "$0" in
-*status)
-       status_only=t
-       ;;
-*commit)
-       status_only=
-       ;;
-esac
-
-refuse_partial () {
-       echo >&2 "$1"
-       echo >&2 "You might have meant to say 'git commit -i paths...', perhaps?"
-       exit 1
-}
-
-TMP_INDEX=
-THIS_INDEX="${GIT_INDEX_FILE:-$GIT_DIR/index}"
-NEXT_INDEX="$GIT_DIR/next-index$$"
-rm -f "$NEXT_INDEX"
-save_index () {
-       cp -p "$THIS_INDEX" "$NEXT_INDEX"
-}
-
-run_status () {
-       # If TMP_INDEX is defined, that means we are doing
-       # "--only" partial commit, and that index file is used
-       # to build the tree for the commit.  Otherwise, if
-       # NEXT_INDEX exists, that is the index file used to
-       # make the commit.  Otherwise we are using as-is commit
-       # so the regular index file is what we use to compare.
-       if test '' != "$TMP_INDEX"
-       then
-               GIT_INDEX_FILE="$TMP_INDEX"
-               export GIT_INDEX_FILE
-       elif test -f "$NEXT_INDEX"
-       then
-               GIT_INDEX_FILE="$NEXT_INDEX"
-               export GIT_INDEX_FILE
-       fi
-
-       if test "$status_only" = "t" -o "$use_status_color" = "t"; then
-               color=
-       else
-               color=--nocolor
-       fi
-       git runstatus ${color} \
-               ${verbose:+--verbose} \
-               ${amend:+--amend} \
-               ${untracked_files:+--untracked}
-}
-
-trap '
-       test -z "$TMP_INDEX" || {
-               test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
-       }
-       rm -f "$NEXT_INDEX"
-' 0
-
-################################################################
-# Command line argument parsing and sanity checking
-
-all=
-also=
-interactive=
-only=
-logfile=
-use_commit=
-amend=
-edit_flag=
-no_edit=
-log_given=
-log_message=
-verify=t
-quiet=
-verbose=
-signoff=
-force_author=
-only_include_assumed=
-untracked_files=
-templatefile="`git config commit.template`"
-while test $# != 0
-do
-       case "$1" in
-       -F|--F|-f|--f|--fi|--fil|--file)
-               case "$#" in 1) usage ;; esac
-               shift
-               no_edit=t
-               log_given=t$log_given
-               logfile="$1"
-               ;;
-       -F*|-f*)
-               no_edit=t
-               log_given=t$log_given
-               logfile="${1#-[Ff]}"
-               ;;
-       --F=*|--f=*|--fi=*|--fil=*|--file=*)
-               no_edit=t
-               log_given=t$log_given
-               logfile="${1#*=}"
-               ;;
-       -a|--a|--al|--all)
-               all=t
-               ;;
-       --au=*|--aut=*|--auth=*|--autho=*|--author=*)
-               force_author="${1#*=}"
-               ;;
-       --au|--aut|--auth|--autho|--author)
-               case "$#" in 1) usage ;; esac
-               shift
-               force_author="$1"
-               ;;
-       -e|--e|--ed|--edi|--edit)
-               edit_flag=t
-               ;;
-       -i|--i|--in|--inc|--incl|--inclu|--includ|--include)
-               also=t
-               ;;
-       --int|--inte|--inter|--intera|--interac|--interact|--interacti|\
-       --interactiv|--interactive)
-               interactive=t
-               ;;
-       -o|--o|--on|--onl|--only)
-               only=t
-               ;;
-       -m|--m|--me|--mes|--mess|--messa|--messag|--message)
-               case "$#" in 1) usage ;; esac
-               shift
-               log_given=m$log_given
-               log_message="${log_message:+${log_message}
-
-}$1"
-               no_edit=t
-               ;;
-       -m*)
-               log_given=m$log_given
-               log_message="${log_message:+${log_message}
-
-}${1#-m}"
-               no_edit=t
-               ;;
-       --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
-               log_given=m$log_given
-               log_message="${log_message:+${log_message}
-
-}${1#*=}"
-               no_edit=t
-               ;;
-       -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\
-       --no-verify)
-               verify=
-               ;;
-       --a|--am|--ame|--amen|--amend)
-               amend=t
-               use_commit=HEAD
-               ;;
-       -c)
-               case "$#" in 1) usage ;; esac
-               shift
-               log_given=t$log_given
-               use_commit="$1"
-               no_edit=
-               ;;
-       --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
-       --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
-       --reedit-messag=*|--reedit-message=*)
-               log_given=t$log_given
-               use_commit="${1#*=}"
-               no_edit=
-               ;;
-       --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
-       --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\
-       --reedit-message)
-               case "$#" in 1) usage ;; esac
-               shift
-               log_given=t$log_given
-               use_commit="$1"
-               no_edit=
-               ;;
-       -C)
-               case "$#" in 1) usage ;; esac
-               shift
-               log_given=t$log_given
-               use_commit="$1"
-               no_edit=t
-               ;;
-       --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
-       --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
-       --reuse-message=*)
-               log_given=t$log_given
-               use_commit="${1#*=}"
-               no_edit=t
-               ;;
-       --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
-       --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
-               case "$#" in 1) usage ;; esac
-               shift
-               log_given=t$log_given
-               use_commit="$1"
-               no_edit=t
-               ;;
-       -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
-               signoff=t
-               ;;
-       -t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template)
-               case "$#" in 1) usage ;; esac
-               shift
-               templatefile="$1"
-               no_edit=
-               ;;
-       -q|--q|--qu|--qui|--quie|--quiet)
-               quiet=t
-               ;;
-       -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
-               verbose=t
-               ;;
-       -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\
-       --untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\
-       --untracked-file|--untracked-files)
-               untracked_files=t
-               ;;
-       --)
-               shift
-               break
-               ;;
-       -*)
-               usage
-               ;;
-       *)
-               break
-               ;;
-       esac
-       shift
-done
-case "$edit_flag" in t) no_edit= ;; esac
-
-################################################################
-# Sanity check options
-
-case "$amend,$initial_commit" in
-t,t)
-       die "You do not have anything to amend." ;;
-t,)
-       if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
-               die "You are in the middle of a merge -- cannot amend."
-       fi ;;
-esac
-
-case "$log_given" in
-tt*)
-       die "Only one of -c/-C/-F can be used." ;;
-*tm*|*mt*)
-       die "Option -m cannot be combined with -c/-C/-F." ;;
-esac
-
-case "$#,$also,$only,$amend" in
-*,t,t,*)
-       die "Only one of --include/--only can be used." ;;
-0,t,,* | 0,,t,)
-       die "No paths with --include/--only does not make sense." ;;
-0,,t,t)
-       only_include_assumed="# Clever... amending the last one with dirty index." ;;
-0,,,*)
-       ;;
-*,,,*)
-       only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..."
-       also=
-       ;;
-esac
-unset only
-case "$all,$interactive,$also,$#" in
-*t,*t,*)
-       die "Cannot use -a, --interactive or -i at the same time." ;;
-t,,,[1-9]*)
-       die "Paths with -a does not make sense." ;;
-,t,,[1-9]*)
-       die "Paths with --interactive does not make sense." ;;
-,,t,0)
-       die "No paths with -i does not make sense." ;;
-esac
-
-if test ! -z "$templatefile" -a -z "$log_given"
-then
-       if test ! -f "$templatefile"
-       then
-               die "Commit template file does not exist."
-       fi
-fi
-
-################################################################
-# Prepare index to have a tree to be committed
-
-case "$all,$also" in
-t,)
-       if test ! -f "$THIS_INDEX"
-       then
-               die 'nothing to commit (use "git add file1 file2" to include for commit)'
-       fi
-       save_index &&
-       (
-               cd_to_toplevel &&
-               GIT_INDEX_FILE="$NEXT_INDEX" &&
-               export GIT_INDEX_FILE &&
-               git diff-files --name-only -z |
-               git update-index --remove -z --stdin
-       ) || exit
-       ;;
-,t)
-       save_index &&
-       git ls-files --error-unmatch -- "$@" >/dev/null || exit
-
-       git diff-files --name-only -z -- "$@"  |
-       (
-               cd_to_toplevel &&
-               GIT_INDEX_FILE="$NEXT_INDEX" &&
-               export GIT_INDEX_FILE &&
-               git update-index --remove -z --stdin
-       ) || exit
-       ;;
-,)
-       if test "$interactive" = t; then
-               git add --interactive || exit
-       fi
-       case "$#" in
-       0)
-               ;; # commit as-is
-       *)
-               if test -f "$GIT_DIR/MERGE_HEAD"
-               then
-                       refuse_partial "Cannot do a partial commit during a merge."
-               fi
-
-               TMP_INDEX="$GIT_DIR/tmp-index$$"
-               W=
-               test -z "$initial_commit" && W=--with-tree=HEAD
-               commit_only=`git ls-files --error-unmatch $W -- "$@"` || exit
-
-               # Build a temporary index and update the real index
-               # the same way.
-               if test -z "$initial_commit"
-               then
-                       GIT_INDEX_FILE="$THIS_INDEX" \
-                       git read-tree --index-output="$TMP_INDEX" -i -m HEAD
-               else
-                       rm -f "$TMP_INDEX"
-               fi || exit
-
-               printf '%s\n' "$commit_only" |
-               GIT_INDEX_FILE="$TMP_INDEX" \
-               git update-index --add --remove --stdin &&
-
-               save_index &&
-               printf '%s\n' "$commit_only" |
-               (
-                       GIT_INDEX_FILE="$NEXT_INDEX"
-                       export GIT_INDEX_FILE
-                       git update-index --add --remove --stdin
-               ) || exit
-               ;;
-       esac
-       ;;
-esac
-
-################################################################
-# If we do as-is commit, the index file will be THIS_INDEX,
-# otherwise NEXT_INDEX after we make this commit.  We leave
-# the index as is if we abort.
-
-if test -f "$NEXT_INDEX"
-then
-       USE_INDEX="$NEXT_INDEX"
-else
-       USE_INDEX="$THIS_INDEX"
-fi
-
-case "$status_only" in
-t)
-       # This will silently fail in a read-only repository, which is
-       # what we want.
-       GIT_INDEX_FILE="$USE_INDEX" git update-index -q --unmerged --refresh
-       run_status
-       exit $?
-       ;;
-'')
-       GIT_INDEX_FILE="$USE_INDEX" git update-index -q --refresh || exit
-       ;;
-esac
-
-################################################################
-# Grab commit message, write out tree and make commit.
-
-if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit
-then
-    GIT_INDEX_FILE="${TMP_INDEX:-${USE_INDEX}}" "$GIT_DIR"/hooks/pre-commit \
-    || exit
-fi
-
-if test "$log_message" != ''
-then
-       printf '%s\n' "$log_message"
-elif test "$logfile" != ""
-then
-       if test "$logfile" = -
-       then
-               test -t 0 &&
-               echo >&2 "(reading log message from standard input)"
-               cat
-       else
-               cat <"$logfile"
-       fi
-elif test "$use_commit" != ""
-then
-       encoding=$(git config i18n.commitencoding || echo UTF-8)
-       git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
-       sed -e '1,/^$/d' -e 's/^    //'
-elif test -f "$GIT_DIR/MERGE_MSG"
-then
-       cat "$GIT_DIR/MERGE_MSG"
-elif test -f "$GIT_DIR/SQUASH_MSG"
-then
-       cat "$GIT_DIR/SQUASH_MSG"
-elif test "$templatefile" != ""
-then
-       cat "$templatefile"
-fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG
-
-case "$signoff" in
-t)
-       sign=$(git-var GIT_COMMITTER_IDENT | sed -e '
-               s/>.*/>/
-               s/^/Signed-off-by: /
-               ')
-       blank_before_signoff=
-       tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
-       grep 'Signed-off-by:' >/dev/null || blank_before_signoff='
-'
-       tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
-       grep "$sign"$ >/dev/null ||
-       printf '%s%s\n' "$blank_before_signoff" "$sign" \
-               >>"$GIT_DIR"/COMMIT_EDITMSG
-       ;;
-esac
-
-if test -f "$GIT_DIR/MERGE_HEAD" && test -z "$no_edit"; then
-       echo "#"
-       echo "# It looks like you may be committing a MERGE."
-       echo "# If this is not correct, please remove the file"
-       printf '%s\n' "#        $GIT_DIR/MERGE_HEAD"
-       echo "# and try again"
-       echo "#"
-fi >>"$GIT_DIR"/COMMIT_EDITMSG
-
-# Author
-if test '' != "$use_commit"
-then
-       eval "$(get_author_ident_from_commit "$use_commit")"
-       export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
-fi
-if test '' != "$force_author"
-then
-       GIT_AUTHOR_NAME=`expr "z$force_author" : 'z\(.*[^ ]\) *<.*'` &&
-       GIT_AUTHOR_EMAIL=`expr "z$force_author" : '.*\(<.*\)'` &&
-       test '' != "$GIT_AUTHOR_NAME" &&
-       test '' != "$GIT_AUTHOR_EMAIL" ||
-       die "malformed --author parameter"
-       export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
-fi
-
-PARENTS="-p HEAD"
-if test -z "$initial_commit"
-then
-       rloga='commit'
-       if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
-               rloga='commit (merge)'
-               PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
-       elif test -n "$amend"; then
-               rloga='commit (amend)'
-               PARENTS=$(git cat-file commit HEAD |
-                       sed -n -e '/^$/q' -e 's/^parent /-p /p')
-       fi
-       current="$(git rev-parse --verify HEAD)"
-else
-       if [ -z "$(git ls-files)" ]; then
-               echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)'
-               exit 1
-       fi
-       PARENTS=""
-       rloga='commit (initial)'
-       current=''
-fi
-set_reflog_action "$rloga"
-
-if test -z "$no_edit"
-then
-       {
-               echo ""
-               echo "# Please enter the commit message for your changes."
-               echo "# (Comment lines starting with '#' will not be included)"
-               test -z "$only_include_assumed" || echo "$only_include_assumed"
-               run_status
-       } >>"$GIT_DIR"/COMMIT_EDITMSG
-else
-       # we need to check if there is anything to commit
-       run_status >/dev/null
-fi
-if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
-then
-       rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
-       use_status_color=t
-       run_status
-       exit 1
-fi
-
-case "$no_edit" in
-'')
-       git-var GIT_AUTHOR_IDENT > /dev/null  || die
-       git-var GIT_COMMITTER_IDENT > /dev/null  || die
-       git_editor "$GIT_DIR/COMMIT_EDITMSG"
-       ;;
-esac
-
-case "$verify" in
-t)
-       if test -x "$GIT_DIR"/hooks/commit-msg
-       then
-               "$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit
-       fi
-esac
-
-if test -z "$no_edit"
-then
-    sed -e '
-        /^diff --git a\/.*/{
-           s///
-           q
-       }
-       /^#/d
-    ' "$GIT_DIR"/COMMIT_EDITMSG
-else
-    cat "$GIT_DIR"/COMMIT_EDITMSG
-fi |
-git stripspace >"$GIT_DIR"/COMMIT_MSG
-
-# Test whether the commit message has any content we didn't supply.
-have_commitmsg=
-grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
-       git stripspace > "$GIT_DIR"/COMMIT_BAREMSG
-
-# Is the commit message totally empty?
-if test -s "$GIT_DIR"/COMMIT_BAREMSG
-then
-       if test "$templatefile" != ""
-       then
-               # Test whether this is just the unaltered template.
-               if cnt=`sed -e '/^#/d' < "$templatefile" |
-                       git stripspace |
-                       diff "$GIT_DIR"/COMMIT_BAREMSG - |
-                       wc -l` &&
-                  test 0 -lt $cnt
-               then
-                       have_commitmsg=t
-               fi
-       else
-               # No template, so the content in the commit message must
-               # have come from the user.
-               have_commitmsg=t
-       fi
-fi
-
-rm -f "$GIT_DIR"/COMMIT_BAREMSG
-
-if test "$have_commitmsg" = "t"
-then
-       if test -z "$TMP_INDEX"
-       then
-               tree=$(GIT_INDEX_FILE="$USE_INDEX" git write-tree)
-       else
-               tree=$(GIT_INDEX_FILE="$TMP_INDEX" git write-tree) &&
-               rm -f "$TMP_INDEX"
-       fi &&
-       commit=$(git commit-tree $tree $PARENTS <"$GIT_DIR/COMMIT_MSG") &&
-       rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
-       git update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" &&
-       rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" &&
-       if test -f "$NEXT_INDEX"
-       then
-               mv "$NEXT_INDEX" "$THIS_INDEX"
-       else
-               : ;# happy
-       fi
-else
-       echo >&2 "* no commit message?  aborting commit."
-       false
-fi
-ret="$?"
-rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
-
-cd_to_toplevel
-
-git rerere
-
-if test "$ret" = 0
-then
-       git gc --auto
-       if test -x "$GIT_DIR"/hooks/post-commit
-       then
-               "$GIT_DIR"/hooks/post-commit
-       fi
-       if test -z "$quiet"
-       then
-               commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\
-                      --summary --root HEAD --`
-               echo "Created${initial_commit:+ initial} commit $commit"
-       fi
-fi
-
-exit "$ret"
diff --git a/git-merge-ours.sh b/git-merge-ours.sh
deleted file mode 100755 (executable)
index c81a790..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-# Pretend we resolved the heads, but declare our tree trumps everybody else.
-#
-
-# We need to exit with 2 if the index does not match our HEAD tree,
-# because the current index is what we will be committing as the
-# merge result.
-
-git diff-index --quiet --cached HEAD || exit 2
-
-exit 0
index 66c80d4e16e3217b93b9de12a4b02d107a2eeb71..bf44b6af58574273df9e7f549136a735f6e549fe 100755 (executable)
@@ -485,8 +485,13 @@ do
                SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
                SHORTHEAD=$(git rev-parse --short $HEAD)
                SHORTONTO=$(git rev-parse --short $ONTO)
-               cat > "$TODO" << EOF
-# Rebasing $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO
+               git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
+                       --abbrev=7 --reverse --left-right --cherry-pick \
+                       $UPSTREAM...$HEAD | \
+                       sed -n "s/^>/pick /p" > "$TODO"
+               cat >> "$TODO" << EOF
+
+# Rebase $SHORTUPSTREAM..$SHORTHEAD onto $SHORTONTO
 #
 # Commands:
 #  pick = use commit
@@ -494,12 +499,9 @@ do
 #  squash = use commit, but meld into previous commit
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
+# However, if you remove everything, the rebase will be aborted.
 #
 EOF
-               git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
-                       --abbrev=7 --reverse --left-right --cherry-pick \
-                       $UPSTREAM...$HEAD | \
-                       sed -n "s/^>/pick /p" >> "$TODO"
 
                has_action "$TODO" ||
                        die_abort "Nothing to do"
diff --git a/git.c b/git.c
index 7604319b5a9e8c12b2f00b4d5e6480445e02daa6..a5adc3487d4ca62a9ff93679ce8711a578a5c3eb 100644 (file)
--- a/git.c
+++ b/git.c
@@ -293,6 +293,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
                { "cherry", cmd_cherry, RUN_SETUP },
                { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
+               { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
                { "config", cmd_config },
                { "count-objects", cmd_count_objects, RUN_SETUP },
@@ -325,6 +326,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "mailsplit", cmd_mailsplit },
                { "merge-base", cmd_merge_base, RUN_SETUP },
                { "merge-file", cmd_merge_file },
+               { "merge-ours", cmd_merge_ours, RUN_SETUP },
                { "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
                { "name-rev", cmd_name_rev, RUN_SETUP },
                { "pack-objects", cmd_pack_objects, RUN_SETUP },
@@ -341,10 +343,10 @@ static void handle_internal_command(int argc, const char **argv)
                { "rev-parse", cmd_rev_parse },
                { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
                { "rm", cmd_rm, RUN_SETUP },
-               { "runstatus", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },
                { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
                { "show-branch", cmd_show_branch, RUN_SETUP },
                { "show", cmd_show, RUN_SETUP | USE_PAGER },
+               { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
                { "stripspace", cmd_stripspace },
                { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
                { "tag", cmd_tag, RUN_SETUP },
diff --git a/ident.c b/ident.c
index 9b2a852cb00327a607946caa864fcf31184da28e..5be7533ffd061bee4d3e1c184c375b095cfed575 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -224,13 +224,17 @@ const char *fmt_ident(const char *name, const char *email,
        }
 
        strcpy(date, git_default_date);
-       if (date_str)
-               parse_date(date_str, date, sizeof(date));
+       if (date_str) {
+               if (*date_str)
+                       parse_date(date_str, date, sizeof(date));
+               else
+                       date[0] = '\0';
+       }
 
        i = copy(buffer, sizeof(buffer), 0, name);
        i = add_raw(buffer, sizeof(buffer), i, " <");
        i = copy(buffer, sizeof(buffer), i, email);
-       i = add_raw(buffer, sizeof(buffer), i, "> ");
+       i = add_raw(buffer, sizeof(buffer), i, date[0] ? "> " : ">");
        i = copy(buffer, sizeof(buffer), i, date);
        if (i >= sizeof(buffer))
                die("Impossibly long personal identifier");
index 13919123dc5261e7b8e4a4fdfc696f2355482b6c..8334a9bad00ddf3e66c18c37020f9ab08691cffa 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -117,5 +117,6 @@ extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
 extern int strbuf_getline(struct strbuf *, FILE *, int);
 
 extern void stripspace(struct strbuf *buf, int skip_comments);
+extern void launch_editor(const char *path, struct strbuf *buffer);
 
 #endif /* STRBUF_H */
index ae0639d8f3a3fa428dca31df55193487a74b5b57..e25b25568337ae36a5ef1377a14df031d5dfaeb1 100755 (executable)
@@ -86,7 +86,7 @@ EOF
 
 git config core.excludesFile excludes-file
 
-git runstatus | grep "^#       " > output
+git status | grep "^#  " > output
 
 cat > expect << EOF
 #      .gitignore
index f1039d1a2146ed68217d849d7588d9ca65af785b..907c7f9f6bccb0ddcf4d9862467a938369a07519 100755 (executable)
@@ -149,7 +149,8 @@ test_expect_success 'stop on conflicting pick' '
        diff -u expect .git/.dotest-merge/patch &&
        diff -u expect2 file1 &&
        test 4 = $(grep -v "^#" < .git/.dotest-merge/done | wc -l) &&
-       test 0 = $(grep -v "^#" < .git/.dotest-merge/git-rebase-todo | wc -l)
+       test 0 = $(grep -ve "^#" -e "^$" < .git/.dotest-merge/git-rebase-todo |
+               wc -l)
 '
 
 test_expect_success 'abort' '
index 063e79257a6e96d9021fa41d18a632560a272776..877c1ea5db4b99f088db6f5e38a6833abc3cfef5 100755 (executable)
@@ -71,10 +71,10 @@ test_expect_success 'favour same basenames over different ones' '
        git rm path1 &&
        mkdir subdir &&
        git mv another-path subdir/path1 &&
-       git runstatus | grep "renamed: .*path1 -> subdir/path1"'
+       git status | grep "renamed: .*path1 -> subdir/path1"'
 
 test_expect_success  'favour same basenames even with minor differences' '
        git show HEAD:path1 | sed "s/15/16/" > subdir/path1 &&
-       git runstatus | grep "renamed: .*path1 -> subdir/path1"'
+       git status | grep "renamed: .*path1 -> subdir/path1"'
 
 test_done
old mode 100644 (file)
new mode 100755 (executable)
index aad863db7ad74ed217b3bcc76de43556f9fe7a07..35889c0a125fffdfb556ec521d61c18ac4e96c17 100755 (executable)
@@ -215,4 +215,17 @@ test_expect_success 'quoting of a strangely named repo' '
        grep "fatal: '\''a\\\\!'\''b'\''" result
 '
 
+test_expect_success 'bundle should record HEAD correctly' '
+
+       cd "$D" &&
+       git bundle create bundle5 HEAD master &&
+       git bundle list-heads bundle5 >actual &&
+       for h in HEAD refs/heads/master
+       do
+               echo "$(git rev-parse --verify $h) $h"
+       done >expect &&
+       diff -u expect actual
+
+'
+
 test_done
index cf389b81da041e6bcbc7d20cd367b4274001353f..baed6ce96beb260a32e027dd573313c82202ea7b 100755 (executable)
@@ -122,7 +122,20 @@ test_expect_success 'using alternate GIT_INDEX_FILE (2)' '
 
        ) &&
        cmp .git/index saved-index >/dev/null
+'
+
+cat > expect << EOF
+zort
+
+Signed-off-by: C O Mitter <committer@example.com>
+EOF
 
+test_expect_success '--signoff' '
+       echo "yet another content *narf*" >> foo &&
+       echo "zort" |
+               GIT_EDITOR=../t7500/add-content git commit -s -F - foo &&
+       git cat-file commit HEAD | sed "1,/^$/d" > output &&
+       diff expect output
 '
 
 test_done
old mode 100644 (file)
new mode 100755 (executable)
index 9dba104..ce83af3
@@ -242,4 +242,29 @@ test_expect_success 'multiple -m' '
 
 '
 
+author="The Real Author <someguy@his.email.org>"
+test_expect_success 'amend commit to fix author' '
+
+       oldtick=$GIT_AUTHOR_DATE &&
+       test_tick &&
+       git reset --hard &&
+       git cat-file -p HEAD |
+       sed -e "s/author.*/author $author $oldtick/" \
+               -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \
+               expected &&
+       git commit --amend --author="$author" &&
+       git cat-file -p HEAD > current &&
+       diff expected current
+
+'
+
+test_expect_success 'git commit <file> with dirty index' '
+       echo tacocat > elif &&
+       echo tehlulz > chz &&
+       git add chz &&
+       git commit elif -m "tacocat is a palindrome" &&
+       git show --stat | grep elif &&
+       git diff --cached | grep chz
+'
+
 test_done
diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
new file mode 100755 (executable)
index 0000000..21ac785
--- /dev/null
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='git commit porcelain-ish'
+
+. ./test-lib.sh
+
+test_expect_success 'the basics' '
+
+       echo doing partial >"commit is" &&
+       mkdir not &&
+       echo very much encouraged but we should >not/forbid &&
+       git add "commit is" not &&
+       echo update added "commit is" file >"commit is" &&
+       echo also update another >not/forbid &&
+       test_tick &&
+       git commit -a -m "initial with -a" &&
+
+       git cat-file blob HEAD:"commit is" >current.1 &&
+       git cat-file blob HEAD:not/forbid >current.2 &&
+
+       cmp current.1 "commit is" &&
+       cmp current.2 not/forbid
+
+'
+
+test_expect_success 'partial' '
+
+       echo another >"commit is" &&
+       echo another >not/forbid &&
+       test_tick &&
+       git commit -m "partial commit to handle a file" "commit is" &&
+
+       changed=$(git diff-tree --name-only HEAD^ HEAD) &&
+       test "$changed" = "commit is"
+
+'
+
+test_expect_success 'partial modification in a subdirecotry' '
+
+       test_tick &&
+       git commit -m "partial commit to subdirectory" not &&
+
+       changed=$(git diff-tree -r --name-only HEAD^ HEAD) &&
+       test "$changed" = "not/forbid"
+
+'
+
+test_expect_success 'partial removal' '
+
+       git rm not/forbid &&
+       git commit -m "partial commit to remove not/forbid" not &&
+
+       changed=$(git diff-tree -r --name-only HEAD^ HEAD) &&
+       test "$changed" = "not/forbid" &&
+       remain=$(git ls-tree -r --name-only HEAD) &&
+       test "$remain" = "commit is"
+
+'
+
+test_expect_success 'sign off' '
+
+       >positive &&
+       git add positive &&
+       git commit -s -m "thank you" &&
+       actual=$(git cat-file commit HEAD | sed -ne "s/Signed-off-by: //p") &&
+       expected=$(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/") &&
+       test "z$actual" = "z$expected"
+
+'
+
+test_expect_success 'multiple -m' '
+
+       >negative &&
+       git add negative &&
+       git commit -m "one" -m "two" -m "three" &&
+       actual=$(git cat-file commit HEAD | sed -e "1,/^\$/d") &&
+       expected=$(echo one; echo; echo two; echo; echo three) &&
+       test "z$actual" = "z$expected"
+
+'
+
+test_expect_success 'verbose' '
+
+       echo minus >negative &&
+       git add negative &&
+       git status -v | sed -ne "/^diff --git /p" >actual &&
+       echo "diff --git a/negative b/negative" >expect &&
+       diff -u expect actual
+
+'
+
+test_done
diff --git a/t/t7502-status.sh b/t/t7502-status.sh
new file mode 100755 (executable)
index 0000000..269b334
--- /dev/null
@@ -0,0 +1,91 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='git-status'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       : > tracked &&
+       : > modified &&
+       mkdir dir1 &&
+       : > dir1/tracked &&
+       : > dir1/modified &&
+       mkdir dir2 &&
+       : > dir1/tracked &&
+       : > dir1/modified &&
+       git add . &&
+       test_tick &&
+       git commit -m initial &&
+       : > untracked &&
+       : > dir1/untracked &&
+       : > dir2/untracked &&
+       echo 1 > dir1/modified &&
+       echo 2 > dir2/modified &&
+       echo 3 > dir2/added &&
+       git add dir2/added
+'
+
+cat > expect << \EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      new file:   dir2/added
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#
+#      modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked
+#      dir2/modified
+#      dir2/untracked
+#      expect
+#      output
+#      untracked
+EOF
+
+test_expect_success 'status' '
+
+       git status > output &&
+       git diff expect output
+
+'
+
+cat > expect << \EOF
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      new file:   ../dir2/added
+#
+# Changed but not updated:
+#   (use "git add <file>..." to update what will be committed)
+#
+#      modified:   ../dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      untracked
+#      ../dir2/modified
+#      ../dir2/untracked
+#      ../expect
+#      ../output
+#      ../untracked
+EOF
+
+test_expect_success 'status with relative paths' '
+
+       (cd dir1 && git status) > output &&
+       git diff expect output
+
+'
+
+test_done
old mode 100644 (file)
new mode 100755 (executable)
index 9a6ef4a89ae659855ae0d2fa3006daf080190ed4..0e0439f2c2d8f19abba0a683ce1b2ab3db6197ba 100644 (file)
@@ -81,33 +81,46 @@ static void wt_status_print_trailer(struct wt_status *s)
        color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
 }
 
-static const char *quote_crlf(const char *in, char *buf, size_t sz)
+static char *quote_path(const char *in, int len,
+               struct strbuf *out, const char *prefix)
 {
-       const char *scan;
-       char *out;
-       const char *ret = in;
+       if (len > 0)
+               strbuf_grow(out, len);
+       strbuf_setlen(out, 0);
+
+       if (prefix) {
+               int off = 0;
+               while (prefix[off] && off < len && prefix[off] == in[off])
+                       if (prefix[off] == '/') {
+                               prefix += off + 1;
+                               in += off + 1;
+                               len -= off + 1;
+                               off = 0;
+                       } else
+                               off++;
+
+               for (; *prefix; prefix++)
+                       if (*prefix == '/')
+                               strbuf_addstr(out, "../");
+       }
 
-       for (scan = in, out = buf; *scan; scan++) {
-               int ch = *scan;
-               int quoted;
+       for (; (len < 0 && *in) || len > 0; in++, len--) {
+               int ch = *in;
 
                switch (ch) {
                case '\n':
-                       quoted = 'n';
+                       strbuf_addstr(out, "\\n");
                        break;
                case '\r':
-                       quoted = 'r';
+                       strbuf_addstr(out, "\\r");
                        break;
                default:
-                       *out++ = ch;
+                       strbuf_addch(out, ch);
                        continue;
                }
-               *out++ = '\\';
-               *out++ = quoted;
-               ret = buf;
        }
-       *out = '\0';
-       return ret;
+
+       return out->buf;
 }
 
 static void wt_status_print_filepair(struct wt_status *s,
@@ -115,10 +128,12 @@ static void wt_status_print_filepair(struct wt_status *s,
 {
        const char *c = color(t);
        const char *one, *two;
-       char onebuf[PATH_MAX], twobuf[PATH_MAX];
+       struct strbuf onebuf, twobuf;
 
-       one = quote_crlf(p->one->path, onebuf, sizeof(onebuf));
-       two = quote_crlf(p->two->path, twobuf, sizeof(twobuf));
+       strbuf_init(&onebuf, 0);
+       strbuf_init(&twobuf, 0);
+       one = quote_path(p->one->path, -1, &onebuf, s->prefix);
+       two = quote_path(p->two->path, -1, &twobuf, s->prefix);
 
        color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
        switch (p->status) {
@@ -150,6 +165,8 @@ static void wt_status_print_filepair(struct wt_status *s,
                die("bug: unhandled diff status %c", p->status);
        }
        fprintf(s->fp, "\n");
+       strbuf_release(&onebuf);
+       strbuf_release(&twobuf);
 }
 
 static void wt_status_print_updated_cb(struct diff_queue_struct *q,
@@ -204,8 +221,9 @@ static void wt_read_cache(struct wt_status *s)
 static void wt_status_print_initial(struct wt_status *s)
 {
        int i;
-       char buf[PATH_MAX];
+       struct strbuf buf;
 
+       strbuf_init(&buf, 0);
        wt_read_cache(s);
        if (active_nr) {
                s->commitable = 1;
@@ -214,11 +232,12 @@ static void wt_status_print_initial(struct wt_status *s)
        for (i = 0; i < active_nr; i++) {
                color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
                color_fprintf_ln(s->fp, color(WT_STATUS_UPDATED), "new file: %s",
-                               quote_crlf(active_cache[i]->name,
-                                          buf, sizeof(buf)));
+                               quote_path(active_cache[i]->name, -1,
+                                          &buf, s->prefix));
        }
        if (active_nr)
                wt_status_print_trailer(s);
+       strbuf_release(&buf);
 }
 
 static void wt_status_print_updated(struct wt_status *s)
@@ -252,7 +271,9 @@ static void wt_status_print_untracked(struct wt_status *s)
        struct dir_struct dir;
        int i;
        int shown_header = 0;
+       struct strbuf buf;
 
+       strbuf_init(&buf, 0);
        memset(&dir, 0, sizeof(dir));
 
        if (!s->untracked) {
@@ -284,20 +305,38 @@ static void wt_status_print_untracked(struct wt_status *s)
                        shown_header = 1;
                }
                color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
-               color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%.*s",
-                               ent->len, ent->name);
+               color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%s",
+                               quote_path(ent->name, ent->len,
+                                       &buf, s->prefix));
        }
+       strbuf_release(&buf);
 }
 
 static void wt_status_print_verbose(struct wt_status *s)
 {
        struct rev_info rev;
+       int saved_stdout;
+
+       fflush(s->fp);
+
+       /* Sigh, the entire diff machinery is hardcoded to output to
+        * stdout.  Do the dup-dance...*/
+       saved_stdout = dup(STDOUT_FILENO);
+       if (saved_stdout < 0 ||dup2(fileno(s->fp), STDOUT_FILENO) < 0)
+               die("couldn't redirect stdout\n");
+
        init_revisions(&rev, NULL);
        setup_revisions(0, NULL, &rev, s->reference);
        rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
        rev.diffopt.detect_rename = 1;
        wt_read_cache(s);
        run_diff_index(&rev, 1);
+
+       fflush(stdout);
+
+       if (dup2(saved_stdout, STDOUT_FILENO) < 0)
+               die("couldn't restore stdout\n");
+       close(saved_stdout);
 }
 
 void wt_status_print(struct wt_status *s)
index 77449326dbec5c3f83559c300aec2922445829ef..225fb4d53561f4f1d6ad43fd990dda1c6da956f0 100644 (file)
@@ -23,9 +23,11 @@ struct wt_status {
        int workdir_untracked;
        const char *index_file;
        FILE *fp;
+       const char *prefix;
 };
 
 int git_status_config(const char *var, const char *value);
+int wt_status_use_color;
 void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);