From: Junio C Hamano Date: Wed, 15 Feb 2006 01:51:50 +0000 (-0800) Subject: Merge branch 'kh/svn' X-Git-Tag: v1.3.0-rc1~211 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/e8a1a11d4e0036a4205dbed533c135b51cd87218?hp=0a48a344c6a886fee7f4e6d88e8ac5d5317ad8eb Merge branch 'kh/svn' * kh/svn: git-svnimport: -r adds svn revision number to commit messages --- diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 00537d8907..8c58685e28 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -24,6 +24,9 @@ OPTIONS ------- The options available are: +--cc:: + Specify a starting "Cc:" value for each email. + --chain-reply-to, --no-chain-reply-to:: If this is set, each email will be sent as a reply to the previous email sent. If disabled with "--no-chain-reply-to", all emails after @@ -48,6 +51,9 @@ The options available are: Only necessary if --compose is also set. If --compose is not set, this will be prompted for. +--no-signed-off-by-cc:: + Do not add emails foudn in Signed-off-by: lines to the cc list. + --quiet:: Make git-send-email less verbose. One line per email should be all that is output. @@ -61,6 +67,10 @@ The options available are: Only necessary if --compose is also set. If --compose is not set, this will be prompted for. +--suppress-from:: + Do not add the From: address to the cc: list, if it shows up in a From: + line. + --to:: Specify the primary recipient of the emails generated. Generally, this will be the upstream maintainer of the diff --git a/combine-diff.c b/combine-diff.c index a38f01b13c..d812600d11 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -630,9 +630,10 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, int i, show_hunks, shown_header = 0; char ourtmp_buf[TMPPATHLEN]; char *ourtmp = ourtmp_buf; + int working_tree_file = !memcmp(elem->sha1, null_sha1, 20); /* Read the result of merge first */ - if (memcmp(elem->sha1, null_sha1, 20)) { + if (!working_tree_file) { result = grab_blob(elem->sha1, &size); write_to_temp_file(ourtmp, result, size); } @@ -646,6 +647,7 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, int len = st.st_size; int cnt = 0; + elem->mode = DIFF_FILE_CANON_MODE(st.st_mode); size = len; result = xmalloc(len + 1); while (cnt < len) { @@ -661,6 +663,7 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, else { /* deleted file */ size = 0; + elem->mode = 0; result = xmalloc(1); result[0] = 0; ourtmp = "/dev/null"; @@ -716,7 +719,7 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, show_hunks = make_hunks(sline, cnt, num_parent, dense); - if (show_hunks || mode_differs) { + if (show_hunks || mode_differs || working_tree_file) { const char *abb; if (header) { @@ -731,8 +734,6 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent, putchar('\n'); printf("index "); for (i = 0; i < num_parent; i++) { - if (elem->parent[i].mode != elem->mode) - mode_differs = 1; abb = find_unique_abbrev(elem->parent[i].sha1, DEFAULT_ABBREV); printf("%s%s", i ? "," : "", abb); diff --git a/diff-files.c b/diff-files.c index 7db5ce6407..0c3f80046b 100644 --- a/diff-files.c +++ b/diff-files.c @@ -150,6 +150,8 @@ int main(int argc, const char **argv) nce->sha1, 20); combine.p.parent[stage-2].mode = DIFF_FILE_CANON_MODE(mode); + combine.p.parent[stage-2].status = + DIFF_STATUS_MODIFIED; } /* diff against the proper unmerged stage */ diff --git a/git-commit.sh b/git-commit.sh index ab5e6bc1c8..f7ee1aadee 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -180,6 +180,7 @@ verify=t verbose= signoff= force_author= +only_include_assumed= while case "$#" in 0) break;; esac do case "$1" in @@ -340,12 +341,8 @@ case "$#,$also$only" in 0,) ;; *,) - echo >&2 "assuming --only paths..." + only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..." also= - - # If we are going to launch an editor, the message won't be - # shown without this... - test -z "$log_given$status_only" && sleep 1 ;; esac unset only @@ -380,6 +377,8 @@ t,) ;; ,t) save_index && + git-ls-files --error-unmatch -- "$@" >/dev/null || exit + git-diff-files --name-only -z -- "$@" | ( cd "$TOP" @@ -408,7 +407,7 @@ t,) refuse_partial "Different in index and the last commit: $dirty_in_index" fi - commit_only=`git-ls-files -- "$@"` + commit_only=`git-ls-files --error-unmatch -- "$@"` || exit # Build the temporary index and update the real index # the same way. @@ -569,7 +568,10 @@ else PARENTS="" fi -run_status >>"$GIT_DIR"/COMMIT_EDITMSG +{ + test -z "$only_include_assumed" || echo "$only_include_assumed" + run_status +} >>"$GIT_DIR"/COMMIT_EDITMSG if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ] then rm -f "$GIT_DIR/COMMIT_EDITMSG" diff --git a/git-rebase.sh b/git-rebase.sh index 16d4359830..f84160d324 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -36,6 +36,15 @@ other=$(git-rev-parse --verify "$1^0") || usage # Make sure the branch to rebase is valid. head=$(git-rev-parse --verify "${2-HEAD}^0") || exit +# If a hook exists, give it a chance to interrupt +if test -x "$GIT_DIR/hooks/pre-rebase" +then + "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || { + echo >&2 "The pre-rebase hook refused to rebase." + exit 1 + } +fi + # If the branch to rebase is given, first switch to it. case "$#" in 2) diff --git a/git-send-email.perl b/git-send-email.perl index 3f1b3ca788..13b85dddd1 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -31,10 +31,10 @@ my $compose_filename = ".msg.$$"; # Variables we fill in automatically, or via prompting: -my (@to,@cc,$initial_reply_to,$initial_subject,@files,$from,$compose); +my (@to,@cc,@initial_cc,$initial_reply_to,$initial_subject,@files,$from,$compose); # Behavior modification variables -my ($chain_reply_to, $smtp_server, $quiet) = (1, "localhost", 0); +my ($chain_reply_to, $smtp_server, $quiet, $suppress_from, $no_signed_off_cc) = (1, "localhost", 0, 0, 0); # Example reply to: #$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>'; @@ -48,10 +48,13 @@ "in-reply-to=s" => \$initial_reply_to, "subject=s" => \$initial_subject, "to=s" => \@to, + "cc=s" => \@initial_cc, "chain-reply-to!" => \$chain_reply_to, "smtp-server=s" => \$smtp_server, "compose" => \$compose, "quiet" => \$quiet, + "suppress-from" => \$suppress_from, + "no-signed-off-cc" => \$no_signed_off_cc, ); # Now, let's fill any that aren't set in with defaults: @@ -197,6 +200,9 @@ --to Specify the primary "To:" line of the email. + --cc Specify an initial "Cc:" list for the entire series + of emails. + --compose Use \$EDITOR to edit an introductory message for the patch series. @@ -212,13 +218,19 @@ email sent, rather than to the first email sent. Defaults to on. + --no-signed-off-cc Suppress the automatic addition of email addresses + that appear in a Signed-off-by: line, to the cc: list. + Note: Using this option is not recommended. + --smtp-server If set, specifies the outgoing SMTP server to use. Defaults to localhost. + --suppress-from Supress sending emails to yourself if your address + appears in a From: line. + --quiet Make git-send-email less verbose. One line per email should be all that is output. - Error: Please specify a file or a directory on the command line. EOT exit(1); @@ -290,7 +302,7 @@ sub send_message foreach my $t (@files) { open(F,"<",$t) or die "can't open file $t"; - @cc = (); + @cc = @initial_cc; my $found_mbox = 0; my $header_done = 0; $message = ""; @@ -304,6 +316,7 @@ sub send_message $subject = $1; } elsif (/^(Cc|From):\s+(.*)$/) { + next if ($2 eq $from && $suppress_from); printf("(mbox) Adding cc: %s from line '%s'\n", $2, $_) unless $quiet; push @cc, $2; @@ -332,7 +345,7 @@ sub send_message } } else { $message .= $_; - if (/^Signed-off-by: (.*)$/i) { + if (/^Signed-off-by: (.*)$/i && !$no_signed_off_cc) { my $c = $1; chomp $c; push @cc, $c; diff --git a/ls-files.c b/ls-files.c index 7024cf18e1..a716e5fad2 100644 --- a/ls-files.c +++ b/ls-files.c @@ -25,6 +25,8 @@ static int line_terminator = '\n'; static int prefix_len = 0, prefix_offset = 0; static const char *prefix = NULL; static const char **pathspec = NULL; +static int error_unmatch = 0; +static char *ps_matched = NULL; static const char *tag_cached = ""; static const char *tag_unmerged = ""; @@ -325,7 +327,8 @@ static int cmp_name(const void *p1, const void *p2) * Match a pathspec against a filename. The first "len" characters * are the common prefix */ -static int match(const char **spec, const char *filename, int len) +static int match(const char **spec, char *ps_matched, + const char *filename, int len) { const char *m; @@ -333,17 +336,24 @@ static int match(const char **spec, const char *filename, int len) int matchlen = strlen(m + len); if (!matchlen) - return 1; + goto matched; if (!strncmp(m + len, filename + len, matchlen)) { if (m[len + matchlen - 1] == '/') - return 1; + goto matched; switch (filename[len + matchlen]) { case '/': case '\0': - return 1; + goto matched; } } if (!fnmatch(m + len, filename + len, 0)) - return 1; + goto matched; + if (ps_matched) + ps_matched++; + continue; + matched: + if (ps_matched) + *ps_matched = 1; + return 1; } return 0; } @@ -356,7 +366,7 @@ static void show_dir_entry(const char *tag, struct nond_on_fs *ent) if (len >= ent->len) die("git-ls-files: internal error - directory entry not superset of prefix"); - if (pathspec && !match(pathspec, ent->name, len)) + if (pathspec && !match(pathspec, ps_matched, ent->name, len)) return; fputs(tag, stdout); @@ -444,7 +454,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, ce->name, len)) + if (pathspec && !match(pathspec, ps_matched, ce->name, len)) return; if (!show_stage) { @@ -699,6 +709,10 @@ int main(int argc, const char **argv) prefix_offset = 0; continue; } + if (!strcmp(arg, "--error-unmatch")) { + error_unmatch = 1; + continue; + } if (*arg == '-') usage(ls_files_usage); break; @@ -710,6 +724,14 @@ int main(int argc, const char **argv) if (pathspec) verify_pathspec(); + /* Treat unmatching pathspec elements as errors */ + if (pathspec && error_unmatch) { + int num; + for (num = 0; pathspec[num]; num++) + ; + ps_matched = xcalloc(1, num); + } + if (show_ignored && !exc_given) { fprintf(stderr, "%s: --ignored needs some exclude pattern\n", argv[0]); @@ -725,5 +747,20 @@ int main(int argc, const char **argv) if (prefix) prune_cache(); show_files(); + + 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++) { + if (ps_matched[num]) + continue; + error("pathspec '%s' did not match any.", + pathspec[num] + prefix_len); + } + return errors ? 1 : 0; + } + return 0; } diff --git a/templates/hooks--pre-rebase b/templates/hooks--pre-rebase new file mode 100644 index 0000000000..981c454cda --- /dev/null +++ b/templates/hooks--pre-rebase @@ -0,0 +1,150 @@ +#!/bin/sh +# +# Copyright (c) 2006 Junio C Hamano +# + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` +fi + +case "$basebranch,$topic" in +master,refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Is topic fully merged to master? +not_in_master=`git-rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git-rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git-rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"` + perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +exit 0 + +################################################################ + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git-rev-list ^master ^topic next + git-rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git-rev-list master..topic + + if this is empty, it is fully merged to "master".