diff options
-rw-r--r-- | Documentation/git-fast-import.txt | 6 | ||||
-rwxr-xr-x | Documentation/install-webdoc.sh | 2 | ||||
-rw-r--r-- | Documentation/sort_glossary.pl | 2 | ||||
-rw-r--r-- | Documentation/user-manual.txt | 45 | ||||
-rw-r--r-- | Makefile | 108 | ||||
-rw-r--r-- | builtin-revert.c | 399 | ||||
-rw-r--r-- | builtin.h | 2 | ||||
-rw-r--r-- | contrib/emacs/git.el | 89 | ||||
-rwxr-xr-x | git-revert.sh | 197 | ||||
-rw-r--r-- | git.c | 2 | ||||
-rw-r--r-- | perl/Makefile | 10 | ||||
-rw-r--r-- | refs.c | 2 | ||||
-rw-r--r-- | setup.c | 2 | ||||
-rw-r--r-- | templates/Makefile | 10 |
14 files changed, 582 insertions, 294 deletions
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt index 7e3d2b1a96..eaba6fd4c1 100644 --- a/Documentation/git-fast-import.txt +++ b/Documentation/git-fast-import.txt @@ -462,7 +462,7 @@ in octal. Git only supports the following modes: In both formats `<path>` is the complete path of the file to be added (if not already existing) or modified (if already existing). -A `<path>` string must use UNIX-style directory seperators (forward +A `<path>` string must use UNIX-style directory separators (forward slash `/`), may contain any byte other than `LF`, and must not start with double quote (`"`). @@ -472,8 +472,8 @@ quoting should be used, e.g. `"path/with\n and \" in it"`. The value of `<path>` must be in canoncial form. That is it must not: * contain an empty directory component (e.g. `foo//bar` is invalid), -* end with a directory seperator (e.g. `foo/` is invalid), -* start with a directory seperator (e.g. `/foo` is invalid), +* end with a directory separator (e.g. `foo/` is invalid), +* start with a directory separator (e.g. `/foo` is invalid), * contain the special component `.` or `..` (e.g. `foo/./bar` and `foo/../bar` are invalid). diff --git a/Documentation/install-webdoc.sh b/Documentation/install-webdoc.sh index b3981936e3..cd3a18eb7f 100755 --- a/Documentation/install-webdoc.sh +++ b/Documentation/install-webdoc.sh @@ -2,7 +2,7 @@ T="$1" -for h in *.html *.txt howto/*.txt howto/*.html RelNotes-*.txt +for h in *.html *.txt howto/*.txt howto/*.html RelNotes-*.txt *.css do if test -f "$T/$h" && diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h" diff --git a/Documentation/sort_glossary.pl b/Documentation/sort_glossary.pl index e0bc552a64..05dc7b2c7b 100644 --- a/Documentation/sort_glossary.pl +++ b/Documentation/sort_glossary.pl @@ -48,7 +48,7 @@ This list is sorted alphabetically: '; @keys=sort {uc($a) cmp uc($b)} keys %terms; -$pattern='(\b(?<!link:git-)'.join('\b|\b(?<!link:git-)',reverse @keys).'\b)'; +$pattern='(\b(?<!link:git-)'.join('\b|\b(?<!-)',reverse @keys).'\b)'; foreach $key (@keys) { $terms{$key}=~s/$pattern/sprintf "<<ref_".no_spaces($1).",$1>>";/eg; print '[[ref_'.no_spaces($key).']]'.$key."::\n" diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index ffd673ec33..d7b227e647 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -437,11 +437,14 @@ We will sometimes represent git history using diagrams like the one below. Commits are shown as "o", and the links between them with lines drawn with - / and \. Time goes left to right: + +................................................ o--o--o <-- Branch A / o--o--o <-- master \ o--o--o <-- Branch B +................................................ If we need to talk about a particular commit, the character "o" may be replaced with another letter or number. @@ -601,8 +604,8 @@ a new stanza: $ cat .git/config ... [remote "linux-nfs"] - url = git://linux-nfs.org/~bfields/git.git - fetch = +refs/heads/*:refs/remotes/linux-nfs-read/* + url = git://linux-nfs.org/pub/nfs-2.6.git + fetch = +refs/heads/*:refs/remotes/linux-nfs/* ... ------------------------------------------------- @@ -1133,17 +1136,9 @@ modified in two different ways in the remote branch and the local branch--then you are warned; the output may look something like this: ------------------------------------------------- -$ git pull . next -Trying really trivial in-index merge... -fatal: Merge requires file-level merging -Nope. -Merging HEAD with 77976da35a11db4580b80ae27e8d65caf5208086 -Merging: -15e2162 world -77976da goodbye -found 1 common ancestor(s): -d122ed4 initial -Auto-merging file.txt +$ git merge next + 100% (4/4) done +Auto-merged file.txt CONFLICT (content): Merge conflict in file.txt Automatic merge failed; fix conflicts and then commit the result. ------------------------------------------------- @@ -1439,7 +1434,7 @@ modifying the working directory, you can do that with gitlink:git-show[1]: ------------------------------------------------- -$ git show HEAD^ path/to/file +$ git show HEAD^:path/to/file ------------------------------------------------- which will display the given version of the file. @@ -1936,25 +1931,29 @@ $ git commit You have performed no merges into mywork, so it is just a simple linear sequence of patches on top of "origin": - +................................................ o--o--o <-- origin \ o--o--o <-- mywork +................................................ Some more interesting work has been done in the upstream project, and "origin" has advanced: +................................................ o--o--O--o--o--o <-- origin \ a--b--c <-- mywork +................................................ At this point, you could use "pull" to merge your changes back in; the result would create a new merge commit, like this: - +................................................ o--o--O--o--o--o <-- origin \ \ a--b--c--m <-- mywork +................................................ However, if you prefer to keep the history in mywork a simple series of commits without any merges, you may instead choose to use @@ -1971,9 +1970,11 @@ point at the latest version of origin, then apply each of the saved patches to the new mywork. The result will look like: +................................................ o--o--O--o--o--o <-- origin \ a'--b'--c' <-- mywork +................................................ In the process, it may discover conflicts. In that case it will stop and allow you to fix the conflicts; after fixing conflicts, use "git @@ -2081,24 +2082,30 @@ The primary problem with rewriting the history of a branch has to do with merging. Suppose somebody fetches your branch and merges it into their branch, with a result something like this: +................................................ o--o--O--o--o--o <-- origin \ \ t--t--t--m <-- their branch: +................................................ Then suppose you modify the last three commits: +................................................ o--o--o <-- new head of origin / o--o--O--o--o--o <-- old head of origin +................................................ If we examined all this history together in one repository, it will look like: +................................................ o--o--o <-- new head of origin / o--o--O--o--o--o <-- old head of origin \ \ t--t--t--m <-- their branch: +................................................ Git has no way of knowing that the new head is an updated version of the old head; it treats this situation exactly the same as it would if @@ -2159,9 +2166,11 @@ commit. Git calls this process a "fast forward". A fast forward looks something like this: +................................................ o--o--o--o <-- old head of the branch \ o--o--o <-- new head of the branch +................................................ In some cases it is possible that the new head will *not* actually be @@ -2169,11 +2178,11 @@ a descendant of the old head. For example, the developer may have realized she made a serious mistake, and decided to backtrack, resulting in a situation like: +................................................ o--o--o--o--a--b <-- old head of the branch \ o--o--o <-- new head of the branch - - +................................................ In this case, "git fetch" will fail, and print out a warning. @@ -1,6 +1,8 @@ # The default target of this Makefile is... all:: +# Define V=1 to have a more verbose compile. +# # Define NO_OPENSSL environment variable if you do not have OpenSSL. # This also implies MOZILLA_SHA1. # @@ -180,7 +182,7 @@ SCRIPT_SH = \ git-merge-one-file.sh git-parse-remote.sh \ git-pull.sh git-rebase.sh \ git-repack.sh git-request-pull.sh git-reset.sh \ - git-revert.sh git-sh-setup.sh \ + git-sh-setup.sh \ git-tag.sh git-verify-tag.sh \ git-applymbox.sh git-applypatch.sh git-am.sh \ git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \ @@ -196,7 +198,7 @@ SCRIPT_PERL = \ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ $(patsubst %.perl,%,$(SCRIPT_PERL)) \ - git-cherry-pick git-status git-instaweb + git-status git-instaweb # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ @@ -223,7 +225,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-fsck-objects$X git-cherry-pick$X \ $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS)) # what 'all' will build and 'install' will install, in gitexecdir @@ -315,6 +317,7 @@ BUILTIN_OBJS = \ builtin-rerere.o \ builtin-rev-list.o \ builtin-rev-parse.o \ + builtin-revert.o \ builtin-rm.o \ builtin-runstatus.o \ builtin-shortlog.o \ @@ -603,6 +606,31 @@ ifdef NO_PERL_MAKEMAKER export NO_PERL_MAKEMAKER endif +QUIET_SUBDIR0 = $(MAKE) -C # space to separate -C and subdir +QUIET_SUBDIR1 = + +ifneq ($(findstring $(MAKEFLAGS),w),w) +PRINT_DIR = --no-print-directory +else # "make -w" +NO_SUBDIR = : +endif + +ifneq ($(findstring $(MAKEFLAGS),s),s) +ifndef V + QUIET_CC = @echo ' ' CC $@; + QUIET_AR = @echo ' ' AR $@; + QUIET_LINK = @echo ' ' LINK $@; + QUIET_BUILT_IN = @echo ' ' BUILTIN $@; + QUIET_GEN = @echo ' ' GEN $@; + QUIET_SUBDIR0 = @subdir= + QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ + $(MAKE) $(PRINT_DIR) -C $$subdir + export V + export QUIET_GEN + export QUIET_BUILT_IN +endif +endif + # Shell quote (do not use $(call) to accommodate ancient setups); SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER)) @@ -637,44 +665,43 @@ ifneq (,$X) endif all:: - $(MAKE) -C git-gui all - $(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all - $(MAKE) -C templates + $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) all + $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all + $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) strip: $(PROGRAMS) git$X $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) GIT-CFLAGS - $(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \ + $(QUIET_LINK)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \ $(ALL_CFLAGS) -o $@ $(filter %.c,$^) \ $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS) help.o: common-cmds.h $(BUILT_INS): git$X - rm -f $@ && ln git$X $@ + $(QUIET_BUILT_IN)rm -f $@ && ln git$X $@ common-cmds.h: Documentation/git-*.txt - ./generate-cmdlist.sh > $@+ - mv $@+ $@ + $(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh - rm -f $@ $@+ + $(QUIET_GEN)rm -f $@ $@+ && \ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ - $@.sh >$@+ - chmod +x $@+ + $@.sh >$@+ && \ + chmod +x $@+ && \ mv $@+ $@ $(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak perl/perl.mak: GIT-CFLAGS - $(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F) + $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F) $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl - rm -f $@ $@+ + $(QUIET_GEN)rm -f $@ $@+ && \ INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \ sed -e '1{' \ -e ' s|#!.*perl|#!$(PERL_PATH_SQ)|' \ @@ -685,20 +712,15 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl -e '}' \ -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ - $@.perl >$@+ - chmod +x $@+ - mv $@+ $@ - -git-cherry-pick: git-revert - cp $< $@+ + $@.perl >$@+ && \ + chmod +x $@+ && \ mv $@+ $@ git-status: git-commit - cp $< $@+ - mv $@+ $@ + $(QUIET_GEN)cp $< $@+ && mv $@+ $@ gitweb/gitweb.cgi: gitweb/gitweb.perl - rm -f $@ $@+ + $(QUIET_GEN)rm -f $@ $@+ && \ sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \ -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \ -e 's|++GIT_BINDIR++|$(bindir)|g' \ @@ -716,12 +738,12 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \ -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \ -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \ - $< >$@+ - chmod +x $@+ + $< >$@+ && \ + chmod +x $@+ && \ mv $@+ $@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css - rm -f $@ $@+ + $(QUIET_GEN)rm -f $@ $@+ && \ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ -e 's/@@NO_CURL@@/$(NO_CURL)/g' \ @@ -729,15 +751,15 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css -e '/@@GITWEB_CGI@@/d' \ -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \ -e '/@@GITWEB_CSS@@/d' \ - $@.sh > $@+ - chmod +x $@+ + $@.sh > $@+ && \ + chmod +x $@+ && \ mv $@+ $@ configure: configure.ac - rm -f $@ $<+ + $(QUIET_GEN)rm -f $@ $<+ && \ sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ - $< > $<+ - autoconf -o $@ $<+ + $< > $<+ && \ + autoconf -o $@ $<+ && \ rm -f $<+ # These can record GIT_VERSION @@ -747,25 +769,25 @@ git$X git.spec \ : GIT-VERSION-FILE %.o: %.c GIT-CFLAGS - $(CC) -o $*.o -c $(ALL_CFLAGS) $< + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $< %.o: %.S - $(CC) -o $*.o -c $(ALL_CFLAGS) $< + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $< exec_cmd.o: exec_cmd.c GIT-CFLAGS - $(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' $< + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' $< builtin-init-db.o: builtin-init-db.c GIT-CFLAGS - $(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $< + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $< http.o: http.c GIT-CFLAGS - $(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $< + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $< ifdef NO_EXPAT http-fetch.o: http-fetch.c http.h GIT-CFLAGS - $(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $< + $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $< endif git-%$X: %.o $(GITLIBS) - $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) ssh-pull.o: ssh-fetch.c ssh-push.o: ssh-upload.c @@ -779,11 +801,11 @@ git-imap-send$X: imap-send.o $(LIB_FILE) http.o http-fetch.o http-push.o: http.h git-http-fetch$X: fetch.o http.o http-fetch.o $(GITLIBS) - $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) git-http-push$X: revision.o http.o http-push.o $(GITLIBS) - $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) $(LIB_OBJS) $(BUILTIN_OBJS) fetch.o: $(LIB_H) @@ -791,7 +813,7 @@ $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) $(DIFF_OBJS): diffcore.h $(LIB_FILE): $(LIB_OBJS) - rm -f $@ && $(AR) rcs $@ $(LIB_OBJS) + $(QUIET_AR)rm -f $@ && $(AR) rcs $@ $(LIB_OBJS) XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \ xdiff/xmerge.o @@ -799,7 +821,7 @@ $(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \ xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h $(XDIFF_LIB): $(XDIFF_OBJS) - rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS) + $(QUIET_AR)rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS) perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS diff --git a/builtin-revert.c b/builtin-revert.c new file mode 100644 index 0000000000..2f2dc1bbaa --- /dev/null +++ b/builtin-revert.c @@ -0,0 +1,399 @@ +#include "cache.h" +#include "builtin.h" +#include "object.h" +#include "commit.h" +#include "tag.h" +#include "wt-status.h" +#include "run-command.h" +#include "exec_cmd.h" +#include "utf8.h" + +/* + * This implements the builtins revert and cherry-pick. + * + * Copyright (c) 2007 Johannes E. Schindelin + * + * Based on git-revert.sh, which is + * + * Copyright (c) 2005 Linus Torvalds + * Copyright (c) 2005 Junio C Hamano + */ + +static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] <commit-ish>"; + +static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-r] [-x] <commit-ish>"; + +static int edit; +static int replay; +enum { REVERT, CHERRY_PICK } action; +static int no_commit; +static struct commit *commit; +static int needed_deref; + +static const char *me; + +#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" + +static void parse_options(int argc, const char **argv) +{ + const char *usage_str = action == REVERT ? + revert_usage : cherry_pick_usage; + unsigned char sha1[20]; + const char *arg; + int i; + + if (argc < 2) + usage(usage_str); + + for (i = 1; i < argc - 1; i++) { + arg = argv[i]; + if (!strcmp(arg, "-n") || !strcmp(arg, "--no-commit")) + no_commit = 1; + else if (!strcmp(arg, "-e") || !strcmp(arg, "--edit")) + edit = 1; + else if (!strcmp(arg, "--no-edit")) + edit = 0; + else if (!strcmp(arg, "-x") || !strcmp(arg, "--i-really-want-" + "to-expose-my-private-commit-object-name")) + replay = 0; + else if (strcmp(arg, "-r")) + usage(usage_str); + } + + arg = argv[argc - 1]; + if (get_sha1(arg, sha1)) + die ("Cannot find '%s'", arg); + commit = (struct commit *)parse_object(sha1); + if (!commit) + die ("Could not find %s", sha1_to_hex(sha1)); + if (commit->object.type == OBJ_TAG) { + commit = (struct commit *) + deref_tag((struct object *)commit, arg, strlen(arg)); + needed_deref = 1; + } + if (commit->object.type != OBJ_COMMIT) + die ("'%s' does not point to a commit", arg); +} + +static char *get_oneline(const char *message) +{ + char *result; + const char *p = message, *abbrev, *eol; + int abbrev_len, oneline_len; + + if (!p) + die ("Could not read commit message of %s", + sha1_to_hex(commit->object.sha1)); + while (*p && (*p != '\n' || p[1] != '\n')) + p++; + + if (*p) { + p += 2; + for (eol = p + 1; *eol && *eol != '\n'; eol++) + ; /* do nothing */ + } else + eol = p; + abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV); + abbrev_len = strlen(abbrev); + oneline_len = eol - p; + result = xmalloc(abbrev_len + 5 + oneline_len); + memcpy(result, abbrev, abbrev_len); + memcpy(result + abbrev_len, "... ", 4); + memcpy(result + abbrev_len + 4, p, oneline_len); + result[abbrev_len + 4 + oneline_len] = '\0'; + return result; +} + +char *get_encoding(const char *message) +{ + const char *p = message, *eol; + + if (!p) + die ("Could not read commit message of %s", + sha1_to_hex(commit->object.sha1)); + while (*p && *p != '\n') { + for (eol = p + 1; *eol && *eol != '\n'; eol++) + ; /* do nothing */ + if (!prefixcmp(p, "encoding ")) { + char *result = xmalloc(eol - 8 - p); + strlcpy(result, p + 9, eol - 8 - p); + return result; + } + p = eol; + if (*p == '\n') + p++; + } + return NULL; +} + +struct lock_file msg_file; +static int msg_fd; + +static void add_to_msg(const char *string) +{ + int len = strlen(string); + if (write_in_full(msg_fd, string, len) < 0) + die ("Could not write to .msg"); +} + +static void add_message_to_msg(const char *message) +{ + const char *p = message; + while (*p && (*p != '\n' || p[1] != '\n')) + p++; + + if (!*p) + add_to_msg(sha1_to_hex(commit->object.sha1)); + + p += 2; + add_to_msg(p); + return; +} + +static void set_author_ident_env(const char *message) +{ + const char *p = message; + if (!p) + die ("Could not read commit message of %s", + sha1_to_hex(commit->object.sha1)); + while (*p && *p != '\n') { + const char *eol; + + for (eol = p; *eol && *eol != '\n'; eol++) + ; /* do nothing */ + if (!prefixcmp(p, "author ")) { + char *line, *pend, *email, *timestamp; + + p += 7; + line = xmalloc(eol + 1 - p); + memcpy(line, p, eol - p); + line[eol - p] = '\0'; + email = strchr(line, '<'); + if (!email) + die ("Could not extract author email from %s", + sha1_to_hex(commit->object.sha1)); + if (email == line) + pend = line; + else + for (pend = email; pend != line + 1 && + isspace(pend[-1]); pend--); + ; /* do nothing */ + *pend = '\0'; + email++; + timestamp = strchr(email, '>'); + if (!timestamp) + die ("Could not extract author email from %s", + sha1_to_hex(commit->object.sha1)); + *timestamp = '\0'; + for (timestamp++; *timestamp && isspace(*timestamp); + timestamp++) + ; /* do nothing */ + setenv("GIT_AUTHOR_NAME", line, 1); + setenv("GIT_AUTHOR_EMAIL", email, 1); + setenv("GIT_AUTHOR_DATE", timestamp, 1); + free(line); + return; + } + p = eol; + if (*p == '\n') + p++; + } + die ("No author information found in %s", + sha1_to_hex(commit->object.sha1)); +} + +static int merge_recursive(const char *base_sha1, + const char *head_sha1, const char *head_name, + const char *next_sha1, const char *next_name) +{ + char buffer[256]; + + sprintf(buffer, "GITHEAD_%s", head_sha1); + setenv(buffer, head_name, 1); + sprintf(buffer, "GITHEAD_%s", next_sha1); + setenv(buffer, next_name, 1); + + /* + * This three way merge is an interesting one. We are at + * $head, and would want to apply the change between $commit + * and $prev on top of us (when reverting), or the change between + * $prev and $commit on top of us (when cherry-picking or replaying). + */ + + return run_command_opt(RUN_COMMAND_NO_STDIN | RUN_GIT_CMD, + "merge-recursive", base_sha1, "--", + head_sha1, next_sha1, NULL); +} + +static int revert_or_cherry_pick(int argc, const char **argv) +{ + unsigned char head[20]; + struct commit *base, *next; + int i; + char *oneline, *encoding, *reencoded_message = NULL; + const char *message; + + git_config(git_default_config); + me = action == REVERT ? "revert" : "cherry-pick"; + setenv(GIT_REFLOG_ACTION, me, 0); + parse_options(argc, argv); + + /* this is copied from the shell script, but it's never triggered... */ + if (action == REVERT && replay) + die("revert is incompatible with replay"); + + if (no_commit) { + /* + * We do not intend to commit immediately. We just want to + * merge the differences in. + */ + if (write_tree(head, 0, NULL)) + die ("Your index file is unmerged."); + } else { + struct wt_status s; + + if (get_sha1("HEAD", head)) + die ("You do not have a valid HEAD"); + wt_status_prepare(&s); + if (s.commitable || s.workdir_dirty) + die ("Dirty index: cannot %s", me); + discard_cache(); + } + + if (!commit->parents) + die ("Cannot %s a root commit", me); + if (commit->parents->next) + die ("Cannot %s a multi-parent commit.", me); + if (!(message = commit->buffer)) + die ("Cannot get commit message for %s", + sha1_to_hex(commit->object.sha1)); + + /* + * "commit" is an existing commit. We would want to apply + * the difference it introduces since its first parent "prev" + * on top of the current HEAD if we are cherry-pick. Or the + * reverse of it if we are revert. + */ + + msg_fd = hold_lock_file_for_update(&msg_file, ".msg", 1); + + encoding = get_encoding(message); + if (!encoding) + encoding = "utf-8"; + if (!git_commit_encoding) + git_commit_encoding = "utf-8"; + if ((reencoded_message = reencode_string(message, + git_commit_encoding, encoding))) + message = reencoded_message; + + oneline = get_oneline(message); + + if (action == REVERT) { + base = commit; + next = commit->parents->item; + add_to_msg("Revert "); + add_to_msg(find_unique_abbrev(commit->object.sha1, + DEFAULT_ABBREV)); + add_to_msg(oneline); + add_to_msg("\nThis reverts commit "); + add_to_msg(sha1_to_hex(commit->object.sha1)); + add_to_msg(".\n"); + } else { + base = commit->parents->item; + next = commit; + set_author_ident_env(message); + add_message_to_msg(message); + if (!replay) { + add_to_msg("(cherry picked from commit "); + add_to_msg(sha1_to_hex(commit->object.sha1)); + add_to_msg(")\n"); + } + } + if (needed_deref) { + add_to_msg("(original 'git "); + add_to_msg(me); + add_to_msg("' arguments: "); + for (i = 0; i < argc; i++) { + if (i) + add_to_msg(" "); + add_to_msg(argv[i]); + } + add_to_msg(")\n"); + } + + if (merge_recursive(sha1_to_hex(base->object.sha1), + sha1_to_hex(head), "HEAD", + sha1_to_hex(next->object.sha1), oneline) || + write_tree(head, 0, NULL)) { + const char *target = git_path("MERGE_MSG"); + add_to_msg("\nConflicts:\n\n"); + read_cache(); + for (i = 0; i < active_nr;) { + struct cache_entry *ce = active_cache[i++]; + if (ce_stage(ce)) { + add_to_msg("\t"); + add_to_msg(ce->name); + add_to_msg("\n"); + while (i < active_nr && !strcmp(ce->name, + active_cache[i]->name)) + i++; + } + } + if (close(msg_fd) || commit_lock_file(&msg_file) < 0) + die ("Error wrapping up .msg"); + unlink(target); + if (rename(".msg", target)) + die ("Could not move .msg to %s", target); + fprintf(stderr, "Automatic %s failed. " + "After resolving the conflicts,\n" + "mark the corrected paths with 'git-add <paths>'\n" + "and commit the result.\n", me); + if (action == CHERRY_PICK) { + fprintf(stderr, "When commiting, use the option " + "'-c %s' to retain authorship and message.\n", + find_unique_abbrev(commit->object.sha1, + DEFAULT_ABBREV)); + } + exit(1); + } + if (close(msg_fd) || commit_lock_file(&msg_file) < 0) + die ("Error wrapping up .msg"); + fprintf(stderr, "Finished one %s.\n", me); + + /* + * + * If we are cherry-pick, and if the merge did not result in + * hand-editing, we will hit this commit and inherit the original + * author date and name. + * If we are revert, or if our cherry-pick results in a hand merge, + * we had better say that the current user is responsible for that. + */ + + if (!no_commit) { + if (edit) + return execl_git_cmd("commit", "-n", "-F", ".msg", + "-e", NULL); + else + return execl_git_cmd("commit", "-n", "-F", ".msg", + NULL); + } + if (reencoded_message) + free(reencoded_message); + + return 0; +} + +int cmd_revert(int argc, const char **argv, const char *prefix) +{ + if (isatty(0)) + edit = 1; + action = REVERT; + return revert_or_cherry_pick(argc, argv); +} + +int cmd_cherry_pick(int argc, const char **argv, const char *prefix) +{ + replay = 1; + action = CHERRY_PICK; + return revert_or_cherry_pick(argc, argv); +} @@ -24,6 +24,7 @@ extern int cmd_cat_file(int argc, const char **argv, const char *prefix); extern int cmd_checkout_index(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_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); @@ -60,6 +61,7 @@ extern int cmd_config(int argc, const char **argv, const char *prefix); extern int cmd_rerere(int argc, const char **argv, const char *prefix); 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); diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 13d198229b..db87a37895 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -891,36 +891,77 @@ and returns the process output as a string." (with-current-buffer log-edit-parent-buffer (git-get-filenames (git-marked-files-state 'added 'deleted 'modified)))) +(defun git-append-sign-off (name email) + "Append a Signed-off-by entry to the current buffer, avoiding duplicates." + (let ((sign-off (format "Signed-off-by: %s <%s>" name email)) + (case-fold-search t)) + (goto-char (point-min)) + (unless (re-search-forward (concat "^" (regexp-quote sign-off)) nil t) + (goto-char (point-min)) + (unless (re-search-forward "^Signed-off-by: " nil t) + (setq sign-off (concat "\n" sign-off))) + (goto-char (point-max)) + (insert sign-off "\n")))) + +(defun git-setup-log-buffer (buffer &optional author-name author-email subject date msg) + "Setup the log buffer for a commit." + (unless git-status (error "Not in git-status buffer.")) + (let ((merge-heads (git-get-merge-heads)) + (dir default-directory) + (committer-name (git-get-committer-name)) + (committer-email (git-get-committer-email)) + (sign-off git-append-signed-off-by)) + (with-current-buffer buffer + (cd dir) + (erase-buffer) + (insert + (propertize + (format "Author: %s <%s>\n%s%s" + (or author-name committer-name) + (or author-email committer-email) + (if date (format "Date: %s\n" date) "") + (if merge-heads + (format "Parent: %s\n%s\n" + (git-rev-parse "HEAD") + (mapconcat (lambda (str) (concat "Parent: " str)) merge-heads "\n")) + "")) + 'face 'git-header-face) + (propertize git-log-msg-separator 'face 'git-separator-face) + "\n") + (when subject (insert subject "\n\n")) + (cond (msg (insert msg "\n")) + ((file-readable-p ".dotest/msg") + (insert-file-contents ".dotest/msg")) + ((file-readable-p ".git/MERGE_MSG") + (insert-file-contents ".git/MERGE_MSG"))) + ; delete empty lines at end + (goto-char (point-min)) + (when (re-search-forward "\n+\\'" nil t) + (replace-match "\n" t t)) + (when sign-off (git-append-sign-off committer-name committer-email))))) + (defun git-commit-file () "Commit the marked file(s), asking for a commit message." (interactive) (unless git-status (error "Not in git-status buffer.")) (let ((buffer (get-buffer-create "*git-commit*")) - (merge-heads (git-get-merge-heads)) - (dir default-directory) (coding-system (git-get-commits-coding-system)) - (sign-off git-append-signed-off-by)) - (with-current-buffer buffer - (when (eq 0 (buffer-size)) - (cd dir) - (erase-buffer) - (insert - (propertize - (format "Author: %s <%s>\n%s" - (git-get-committer-name) (git-get-committer-email) - (if merge-heads - (format "Parent: %s\n%s\n" - (git-rev-parse "HEAD") - (mapconcat (lambda (str) (concat "Parent: " str)) merge-heads "\n")) - "")) - 'face 'git-header-face) - (propertize git-log-msg-separator 'face 'git-separator-face) - "\n") - (cond ((file-readable-p ".git/MERGE_MSG") - (insert-file-contents ".git/MERGE_MSG")) - (sign-off - (insert (format "\n\nSigned-off-by: %s <%s>\n" - (git-get-committer-name) (git-get-committer-email))))))) + author-name author-email subject date) + (when (eq 0 (buffer-size buffer)) + (when (file-readable-p ".dotest/info") + (with-temp-buffer + (insert-file-contents ".dotest/info") + (goto-char (point-min)) + (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t) + (setq author-name (match-string 1)) + (setq author-email (match-string 2))) + (goto-char (point-min)) + (when (re-search-forward "^Subject: \\(.*\\)$" nil t) + (setq subject (match-string 1))) + (goto-char (point-min)) + (when (re-search-forward "^Date: \\(.*\\)$" nil t) + (setq date (match-string 1))))) + (git-setup-log-buffer buffer author-name author-email subject date)) (log-edit #'git-do-commit nil #'git-log-edit-files buffer) (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) (setq buffer-file-coding-system coding-system) diff --git a/git-revert.sh b/git-revert.sh deleted file mode 100755 index 49f00321b2..0000000000 --- a/git-revert.sh +++ /dev/null @@ -1,197 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Linus Torvalds -# Copyright (c) 2005 Junio C Hamano -# - -case "$0" in -*-revert* ) - test -t 0 && edit=-e - replay= - me=revert - USAGE='[--edit | --no-edit] [-n] <commit-ish>' ;; -*-cherry-pick* ) - replay=t - edit= - me=cherry-pick - USAGE='[--edit] [-n] [-r] [-x] <commit-ish>' ;; -* ) - echo >&2 "What are you talking about?" - exit 1 ;; -esac - -SUBDIRECTORY_OK=Yes ;# we will cd up -. git-sh-setup -require_work_tree -cd_to_toplevel - -no_commit= -while case "$#" in 0) break ;; esac -do - case "$1" in - -n|--n|--no|--no-|--no-c|--no-co|--no-com|--no-comm|\ - --no-commi|--no-commit) - no_commit=t - ;; - -e|--e|--ed|--edi|--edit) - edit=-e - ;; - --n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit) - edit= - ;; - -r) - : no-op ;; - -x|--i-really-want-to-expose-my-private-commit-object-name) - replay= - ;; - -*) - usage - ;; - *) - break - ;; - esac - shift -done - -set_reflog_action "$me" - -test "$me,$replay" = "revert,t" && usage - -case "$no_commit" in -t) - # We do not intend to commit immediately. We just want to - # merge the differences in. - head=$(git-write-tree) || - die "Your index file is unmerged." - ;; -*) - head=$(git-rev-parse --verify HEAD) || - die "You do not have a valid HEAD" - files=$(git-diff-index --cached --name-only $head) || exit - if [ "$files" ]; then - die "Dirty index: cannot $me (dirty: $files)" - fi - ;; -esac - -rev=$(git-rev-parse --verify "$@") && -commit=$(git-rev-parse --verify "$rev^0") || - die "Not a single commit $@" -prev=$(git-rev-parse --verify "$commit^1" 2>/dev/null) || - die "Cannot run $me a root commit" -git-rev-parse --verify "$commit^2" >/dev/null 2>&1 && - die "Cannot run $me a multi-parent commit." - -encoding=$(git config i18n.commitencoding || echo UTF-8) - -# "commit" is an existing commit. We would want to apply -# the difference it introduces since its first parent "prev" -# on top of the current HEAD if we are cherry-pick. Or the -# reverse of it if we are revert. - -case "$me" in -revert) - git show -s --pretty=oneline --encoding="$encoding" $commit | - sed -e ' - s/^[^ ]* /Revert "/ - s/$/"/ - ' - echo - echo "This reverts commit $commit." - test "$rev" = "$commit" || - echo "(original 'git revert' arguments: $@)" - base=$commit next=$prev - ;; - -cherry-pick) - pick_author_script=' - /^author /{ - s/'\''/'\''\\'\'\''/g - h - s/^author \([^<]*\) <[^>]*> .*$/\1/ - s/'\''/'\''\'\'\''/g - s/.*/GIT_AUTHOR_NAME='\''&'\''/p - - g - s/^author [^<]* <\([^>]*\)> .*$/\1/ - s/'\''/'\''\'\'\''/g - s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p - - g - s/^author [^<]* <[^>]*> \(.*\)$/\1/ - s/'\''/'\''\'\'\''/g - s/.*/GIT_AUTHOR_DATE='\''&'\''/p - - q - }' - - logmsg=`git show -s --pretty=raw --encoding="$encoding" "$commit"` - set_author_env=`echo "$logmsg" | - LANG=C LC_ALL=C sed -ne "$pick_author_script"` - eval "$set_author_env" - export GIT_AUTHOR_NAME - export GIT_AUTHOR_EMAIL - export GIT_AUTHOR_DATE - - echo "$logmsg" | - sed -e '1,/^$/d' -e 's/^ //' - case "$replay" in - '') - echo "(cherry picked from commit $commit)" - test "$rev" = "$commit" || - echo "(original 'git cherry-pick' arguments: $@)" - ;; - esac - base=$prev next=$commit - ;; - -esac >.msg - -eval GITHEAD_$head=HEAD -eval GITHEAD_$next='`git show -s \ - --pretty=oneline --encoding="$encoding" "$commit" | - sed -e "s/^[^ ]* //"`' -export GITHEAD_$head GITHEAD_$next - -# This three way merge is an interesting one. We are at -# $head, and would want to apply the change between $commit -# and $prev on top of us (when reverting), or the change between -# $prev and $commit on top of us (when cherry-picking or replaying). - -git-merge-recursive $base -- $head $next && -result=$(git-write-tree 2>/dev/null) || { - mv -f .msg "$GIT_DIR/MERGE_MSG" - { - echo ' -Conflicts: -' - git ls-files --unmerged | - sed -e 's/^[^ ]* / /' | - uniq - } >>"$GIT_DIR/MERGE_MSG" - echo >&2 "Automatic $me failed. After resolving the conflicts," - echo >&2 "mark the corrected paths with 'git-add <paths>'" - echo >&2 "and commit the result." - case "$me" in - cherry-pick) - echo >&2 "You may choose to use the following when making" - echo >&2 "the commit:" - echo >&2 "$set_author_env" - esac - exit 1 -} -echo >&2 "Finished one $me." - -# If we are cherry-pick, and if the merge did not result in -# hand-editing, we will hit this commit and inherit the original -# author date and name. -# If we are revert, or if our cherry-pick results in a hand merge, -# we had better say that the current user is responsible for that. - -case "$no_commit" in -'') - git-commit -n -F .msg $edit - rm -f .msg - ;; -esac @@ -234,6 +234,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "checkout-index", cmd_checkout_index, RUN_SETUP }, { "check-ref-format", cmd_check_ref_format }, { "cherry", cmd_cherry, RUN_SETUP }, + { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NOT_BARE }, { "commit-tree", cmd_commit_tree, RUN_SETUP }, { "config", cmd_config }, { "count-objects", cmd_count_objects, RUN_SETUP }, @@ -272,6 +273,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "rerere", cmd_rerere, RUN_SETUP }, { "rev-list", cmd_rev_list, RUN_SETUP }, { "rev-parse", cmd_rev_parse, RUN_SETUP }, + { "revert", cmd_revert, RUN_SETUP | NOT_BARE }, { "rm", cmd_rm, RUN_SETUP | NOT_BARE }, { "runstatus", cmd_runstatus, RUN_SETUP | NOT_BARE }, { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER }, diff --git a/perl/Makefile b/perl/Makefile index 099beda873..5ec0389883 100644 --- a/perl/Makefile +++ b/perl/Makefile @@ -6,11 +6,15 @@ makfile:=perl.mak PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH)) prefix_SQ = $(subst ','\'',$(prefix)) +ifndef V + QUIET = @ +endif + all install instlibdir: $(makfile) - $(MAKE) -f $(makfile) $@ + $(QUIET)$(MAKE) -f $(makfile) $@ clean: - test -f $(makfile) && $(MAKE) -f $(makfile) $@ || exit 0 + $(QUIET)test -f $(makfile) && $(MAKE) -f $(makfile) $@ || exit 0 $(RM) ppport.h $(RM) $(makfile) $(RM) $(makfile).old @@ -29,7 +33,7 @@ $(makfile): ../GIT-CFLAGS Makefile echo ' echo $(instdir_SQ)' >> $@ else $(makfile): Makefile.PL ../GIT-CFLAGS - '$(PERL_PATH_SQ)' $< PREFIX='$(prefix_SQ)' + $(QUIET_GEN)'$(PERL_PATH_SQ)' $< PREFIX='$(prefix_SQ)' endif # this is just added comfort for calling make directly in perl dir @@ -921,6 +921,8 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1, log_file, strerror(errno)); } + adjust_shared_perm(log_file); + msglen = 0; if (msg) { /* clean up the message and make sure it is a single line */ @@ -216,7 +216,7 @@ const char *setup_git_directory_gently(int *nongit_ok) die("Not a git repository: '%s'", gitdirenv); } - if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/') + if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/') die("Unable to read current working directory"); offset = len = strlen(cwd); diff --git a/templates/Makefile b/templates/Makefile index 0eeee43feb..b8352e731b 100644 --- a/templates/Makefile +++ b/templates/Makefile @@ -1,5 +1,9 @@ # make and install sample templates +ifndef V + QUIET = @ +endif + INSTALL ?= install TAR ?= tar prefix ?= $(HOME) @@ -18,7 +22,7 @@ all: boilerplates.made custom bpsrc = $(filter-out %~,$(wildcard *--*)) boilerplates.made : $(bpsrc) - ls *--* 2>/dev/null | \ + $(QUIET)ls *--* 2>/dev/null | \ while read boilerplate; \ do \ case "$$boilerplate" in *~) continue ;; esac && \ @@ -29,13 +33,13 @@ boilerplates.made : $(bpsrc) *--) ;; \ *) cp $$boilerplate blt/$$dst ;; \ esac || exit; \ - done || exit + done && \ date >$@ # If you need build-tailored templates, build them into blt/ # directory yourself here. custom: - : no custom templates yet + $(QUIET): no custom templates yet clean: rm -rf blt boilerplates.made |