From a9cc857ada7c57069ff00eed8d0addcf55849f39 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Nov 2007 00:24:27 -0700 Subject: War on whitespace: first, a bit of retreat. This introduces core.whitespace configuration variable that lets you specify the definition of "whitespace error". Currently there are two kinds of whitespace errors defined: * trailing-space: trailing whitespaces at the end of the line. * space-before-tab: a SP appears immediately before HT in the indent part of the line. You can specify the desired types of errors to be detected by listing their names (unique abbreviations are accepted) separated by comma. By default, these two errors are always detected, as that is the traditional behaviour. You can disable detection of a particular type of error by prefixing a '-' in front of the name of the error, like this: [core] whitespace = -trailing-space This patch teaches the code to output colored diff with DIFF_WHITESPACE color to highlight the detected whitespace errors to honor the new configuration. Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index bfffa05..a6e5988 100644 --- a/cache.h +++ b/cache.h @@ -602,4 +602,13 @@ extern int diff_auto_refresh_index; /* match-trees.c */ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int); +/* + * whitespace rules. + * used by both diff and apply + */ +#define WS_TRAILING_SPACE 01 +#define WS_SPACE_BEFORE_TAB 02 +#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB) +extern unsigned whitespace_rule; + #endif /* CACHE_H */ diff --git a/config.c b/config.c index dc3148d..ffb418c 100644 --- a/config.c +++ b/config.c @@ -246,6 +246,53 @@ static unsigned long get_unit_factor(const char *end) die("unknown unit: '%s'", end); } +static struct whitespace_rule { + const char *rule_name; + unsigned rule_bits; +} whitespace_rule_names[] = { + { "trailing-space", WS_TRAILING_SPACE }, + { "space-before-tab", WS_SPACE_BEFORE_TAB }, +}; + +static unsigned parse_whitespace_rule(const char *string) +{ + unsigned rule = WS_DEFAULT_RULE; + + while (string) { + int i; + size_t len; + const char *ep; + int negated = 0; + + string = string + strspn(string, ", \t\n\r"); + ep = strchr(string, ','); + if (!ep) + len = strlen(string); + else + len = ep - string; + + if (*string == '-') { + negated = 1; + string++; + len--; + } + if (!len) + break; + for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++) { + if (strncmp(whitespace_rule_names[i].rule_name, + string, len)) + continue; + if (negated) + rule &= ~whitespace_rule_names[i].rule_bits; + else + rule |= whitespace_rule_names[i].rule_bits; + break; + } + string = ep; + } + return rule; +} + int git_parse_long(const char *value, long *ret) { if (value && *value) { @@ -431,6 +478,11 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.whitespace")) { + whitespace_rule = parse_whitespace_rule(value); + return 0; + } + /* Add other config variables here and to Documentation/config.txt. */ return 0; } diff --git a/diff.c b/diff.c index a6aaaf7..2112353 100644 --- a/diff.c +++ b/diff.c @@ -508,7 +508,8 @@ static void emit_line_with_ws(int nparents, for (i = col0; i < len; i++) { if (line[i] == '\t') { last_tab_in_indent = i; - if (0 <= last_space_in_indent) + if ((whitespace_rule & WS_SPACE_BEFORE_TAB) && + 0 <= last_space_in_indent) need_highlight_leading_space = 1; } else if (line[i] == ' ') @@ -540,10 +541,12 @@ static void emit_line_with_ws(int nparents, tail = len - 1; if (line[tail] == '\n' && i < tail) tail--; - while (i < tail) { - if (!isspace(line[tail])) - break; - tail--; + if (whitespace_rule & WS_TRAILING_SPACE) { + while (i < tail) { + if (!isspace(line[tail])) + break; + tail--; + } } if ((i < tail && line[tail + 1] != '\n')) { /* This has whitespace between tail+1..len */ diff --git a/environment.c b/environment.c index b5a6c69..624dd96 100644 --- a/environment.c +++ b/environment.c @@ -35,6 +35,7 @@ int pager_in_use; int pager_use_color = 1; char *editor_program; int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ +unsigned whitespace_rule = WS_DEFAULT_RULE; /* This is set by setup_git_dir_gently() and/or git_default_config() */ char *git_work_tree_cfg; -- cgit v0.10.2-6-g49f6 From 459fa6d0fe6a45b8b120463b56a68e943e3a8101 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 Oct 2007 18:00:27 -0700 Subject: git-diff: complain about >=8 consecutive spaces in initial indent This introduces a new whitespace error type, "indent-with-non-tab". The error is about starting a line with 8 or more SP, instead of indenting it with a HT. This is not enabled by default, as some projects employ an indenting policy to use only SPs and no HTs. The kernel folks and git contributors may want to enable this detection with: [core] whitespace = indent-with-non-tab Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index a6e5988..3f42827 100644 --- a/cache.h +++ b/cache.h @@ -608,6 +608,7 @@ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, i */ #define WS_TRAILING_SPACE 01 #define WS_SPACE_BEFORE_TAB 02 +#define WS_INDENT_WITH_NON_TAB 04 #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB) extern unsigned whitespace_rule; diff --git a/config.c b/config.c index ffb418c..d5b9766 100644 --- a/config.c +++ b/config.c @@ -252,6 +252,7 @@ static struct whitespace_rule { } whitespace_rule_names[] = { { "trailing-space", WS_TRAILING_SPACE }, { "space-before-tab", WS_SPACE_BEFORE_TAB }, + { "indent-with-non-tab", WS_INDENT_WITH_NON_TAB }, }; static unsigned parse_whitespace_rule(const char *string) diff --git a/diff.c b/diff.c index 2112353..6bb902f 100644 --- a/diff.c +++ b/diff.c @@ -502,8 +502,11 @@ static void emit_line_with_ws(int nparents, int i; int tail = len; int need_highlight_leading_space = 0; - /* The line is a newly added line. Does it have funny leading - * whitespaces? In indent, SP should never precede a TAB. + /* + * The line is a newly added line. Does it have funny leading + * whitespaces? In indent, SP should never precede a TAB. In + * addition, under "indent with non tab" rule, there should not + * be more than 8 consecutive spaces. */ for (i = col0; i < len; i++) { if (line[i] == '\t') { @@ -517,6 +520,13 @@ static void emit_line_with_ws(int nparents, else break; } + if ((whitespace_rule & WS_INDENT_WITH_NON_TAB) && + 0 <= last_space_in_indent && + last_tab_in_indent < 0 && + 8 <= (i - col0)) { + last_tab_in_indent = i; + need_highlight_leading_space = 1; + } fputs(set, stdout); fwrite(line, col0, 1, stdout); fputs(reset, stdout); -- cgit v0.10.2-6-g49f6 From 49e703afda0e4e67050fcc8e05b175987c83391c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 2 Nov 2007 17:46:55 -0700 Subject: core.whitespace: add test for diff whitespace error highlighting This tests seletive enabling/disabling of whitespace error highlighting done by colored diff output. Signed-off-by: Junio C Hamano diff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh new file mode 100755 index 0000000..dbc895b --- /dev/null +++ b/t/t4019-diff-wserror.sh @@ -0,0 +1,76 @@ +#!/bin/sh + +test_description='diff whitespace error detection' + +. ./test-lib.sh + +test_expect_success setup ' + + git config diff.color.whitespace "blue reverse" && + >F && + git add F && + echo " Eight SP indent" >>F && + echo " HT and SP indent" >>F && + echo "With trailing SP " >>F && + echo "No problem" >>F + +' + +blue_grep='7;34m' ;# ESC [ 7 ; 3 4 m + +test_expect_success default ' + + git diff --color >output + grep "$blue_grep" output >error + grep -v "$blue_grep" output >normal + + grep Eight normal >/dev/null && + grep HT error >/dev/null && + grep With error >/dev/null && + grep No normal >/dev/null + +' + +test_expect_success 'without -trail' ' + + git config core.whitespace -trail + git diff --color >output + grep "$blue_grep" output >error + grep -v "$blue_grep" output >normal + + grep Eight normal >/dev/null && + grep HT error >/dev/null && + grep With normal >/dev/null && + grep No normal >/dev/null + +' + +test_expect_success 'without -space' ' + + git config core.whitespace -space + git diff --color >output + grep "$blue_grep" output >error + grep -v "$blue_grep" output >normal + + grep Eight normal >/dev/null && + grep HT normal >/dev/null && + grep With error >/dev/null && + grep No normal >/dev/null + +' + +test_expect_success 'with indent-non-tab only' ' + + git config core.whitespace indent,-trailing,-space + git diff --color >output + grep "$blue_grep" output >error + grep -v "$blue_grep" output >normal + + grep Eight error >/dev/null && + grep HT normal >/dev/null && + grep With normal >/dev/null && + grep No normal >/dev/null + +' + +test_done -- cgit v0.10.2-6-g49f6 From 1e8a1954519a070e92cb73f756b271664e1cc4d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 3 Nov 2007 20:18:06 +0700 Subject: Add missing inside_work_tree setting in setup_git_directory_gently MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When both GIT_DIR and GIT_WORK_TREE are set, and setup_git_directory_gently() changes the current working directory accordingly, it should also set inside_work_tree = 1. Without this, work_tree handling code in setup_git_directory() will be activated. If you stay in root work tree (no prefix), it does not harm. It does if you work from a subdirectory though. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/setup.c b/setup.c index 145eca5..6f8f769 100644 --- a/setup.c +++ b/setup.c @@ -240,6 +240,7 @@ const char *setup_git_directory_gently(int *nongit_ok) if (chdir(work_tree_env) < 0) die ("Could not chdir to %s", work_tree_env); strcat(buffer, "/"); + inside_work_tree = 1; return retval; } if (nongit_ok) { -- cgit v0.10.2-6-g49f6 From 47d996a20c3347bb9efbb44e8ed2d615cfdffba3 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 11 Nov 2007 15:35:07 +0100 Subject: push: support pushing HEAD to real branch name This teaches "push HEAD" to resolve HEAD on the local side to its real branch name, e.g. master, and then act as if the real branch name was specified. So we have a shorthand for pushing the current branch. Besides HEAD, no other symbolic ref is resolved. Thanks to Daniel Barkalow for suggesting this implementation, which is much simpler than the implementation proposed before. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano diff --git a/builtin-push.c b/builtin-push.c index 6d1da07..54fba0e 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -44,6 +44,15 @@ static void set_refspecs(const char **refs, int nr) strcat(tag, refs[i]); ref = tag; } + if (!strcmp("HEAD", ref)) { + unsigned char sha1_dummy[20]; + ref = resolve_ref(ref, sha1_dummy, 1, NULL); + if (!ref) + die("HEAD cannot be resolved."); + if (prefixcmp(ref, "refs/heads/")) + die("HEAD cannot be resolved to branch."); + ref = xstrdup(ref + 11); + } add_refspec(ref); } } diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 86f9b53..b0ff488 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -244,6 +244,23 @@ test_expect_success 'push with colon-less refspec (4)' ' ' +test_expect_success 'push with HEAD' ' + + mk_test heads/master && + git checkout master && + git push testrepo HEAD && + check_push_result $the_commit heads/master + +' + +test_expect_success 'push with HEAD nonexisting at remote' ' + + mk_test heads/master && + git checkout -b local master && + git push testrepo HEAD && + check_push_result $the_commit heads/local +' + test_expect_success 'push with dry-run' ' mk_test heads/master && -- cgit v0.10.2-6-g49f6 From 79803322c1d8d2f74e1a53d44f363d878180e0f5 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 11 Nov 2007 15:01:46 +0100 Subject: add refname_match() We use at least two rulesets for matching abbreviated refnames with full refnames (starting with 'refs/'). git-rev-parse and git-fetch use slightly different rules. This commit introduces a new function refname_match (const char *abbrev_name, const char *full_name, const char **rules). abbrev_name is expanded using the rules and matched against full_name. If a match is found the function returns true. rules is a NULL-terminate list of format patterns with "%.*s", for example: const char *ref_rev_parse_rules[] = { "%.*s", "refs/%.*s", "refs/tags/%.*s", "refs/heads/%.*s", "refs/remotes/%.*s", "refs/remotes/%.*s/HEAD", NULL }; Asterisks are included in the format strings because this is the form required in sha1_name.c. Sharing the list with the functions there is a good idea to avoid duplicating the rules. Hopefully this facilitates unified matching rules in the future. This commit makes the rules used by rev-parse for resolving refs to sha1s available for string comparison. Before this change, the rules were buried in get_sha1*() and dwim_ref(). A follow-up commit will refactor the rules used by fetch. refname_match() will be used for matching refspecs in git-send-pack. Thanks to Daniel Barkalow for pointing out that ref_matches_abbrev in remote.c solves a similar problem and care should be taken to avoid confusion. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index 33ebccf..38d9a28 100644 --- a/cache.h +++ b/cache.h @@ -415,6 +415,9 @@ extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int * extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); +extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules); +extern const char *ref_rev_parse_rules[]; + extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg); extern int validate_headref(const char *ref); diff --git a/refs.c b/refs.c index ae53254..fc26a93 100644 --- a/refs.c +++ b/refs.c @@ -643,6 +643,30 @@ int check_ref_format(const char *ref) } } +const char *ref_rev_parse_rules[] = { + "%.*s", + "refs/%.*s", + "refs/tags/%.*s", + "refs/heads/%.*s", + "refs/remotes/%.*s", + "refs/remotes/%.*s/HEAD", + NULL +}; + +int refname_match(const char *abbrev_name, const char *full_name, const char **rules) +{ + const char **p; + const int abbrev_name_len = strlen(abbrev_name); + + for (p = rules; *p; p++) { + if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) { + return 1; + } + } + + return 0; +} + static struct ref_lock *verify_lock(struct ref_lock *lock, const unsigned char *old_sha1, int mustexist) { diff --git a/sha1_name.c b/sha1_name.c index 2d727d5..d364244 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -239,23 +239,13 @@ static int ambiguous_path(const char *path, int len) return slash; } -static const char *ref_fmt[] = { - "%.*s", - "refs/%.*s", - "refs/tags/%.*s", - "refs/heads/%.*s", - "refs/remotes/%.*s", - "refs/remotes/%.*s/HEAD", - NULL -}; - int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) { const char **p, *r; int refs_found = 0; *ref = NULL; - for (p = ref_fmt; *p; p++) { + for (p = ref_rev_parse_rules; *p; p++) { unsigned char sha1_from_ref[20]; unsigned char *this_result; @@ -277,7 +267,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) int logs_found = 0; *log = NULL; - for (p = ref_fmt; *p; p++) { + for (p = ref_rev_parse_rules; *p; p++) { struct stat st; unsigned char hash[20]; char path[PATH_MAX]; -- cgit v0.10.2-6-g49f6 From ae36bdcf5147b1b54de852eda111ad76a3040726 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 11 Nov 2007 15:01:47 +0100 Subject: push: use same rules as git-rev-parse to resolve refspecs This commit changes the rules for resolving refspecs to match the rules for resolving refs in rev-parse. git-rev-parse uses clear rules to resolve a short ref to its full name, which are well documented. The rules for resolving refspecs documented in git-send-pack were less strict and harder to understand. This commit replaces them by the rules of git-rev-parse. The unified rules are easier to understand and better resolve ambiguous cases. You can now push from a repository containing several branches ending on the same short name. Note, this may break existing setups. For example, "master" will no longer resolve to "origin/master" even when there is no other "master" elsewhere. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt index 2fa01d4..a2d9cb6 100644 --- a/Documentation/git-send-pack.txt +++ b/Documentation/git-send-pack.txt @@ -85,7 +85,9 @@ Each pattern pair consists of the source side (before the colon) and the destination side (after the colon). The ref to be pushed is determined by finding a match that matches the source side, and where it is pushed is determined by using the -destination side. +destination side. The rules used to match a ref are the same +rules used by gitlink:git-rev-parse[1] to resolve a symbolic ref +name. - It is an error if does not match exactly one of the local refs. diff --git a/remote.c b/remote.c index bec2ba1..4085c51 100644 --- a/remote.c +++ b/remote.c @@ -519,10 +519,7 @@ static int count_refspec_match(const char *pattern, char *name = refs->name; int namelen = strlen(name); - if (namelen < patlen || - memcmp(name + namelen - patlen, pattern, patlen)) - continue; - if (namelen != patlen && name[namelen - patlen - 1] != '/') + if (!refname_match(pattern, name, ref_rev_parse_rules)) continue; /* A match is "weak" if it is with refs outside diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index b0ff488..fd5f284 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -145,11 +145,21 @@ test_expect_success 'push with no ambiguity (1)' ' test_expect_success 'push with no ambiguity (2)' ' mk_test remotes/origin/master && - git push testrepo master:master && + git push testrepo master:origin/master && check_push_result $the_commit remotes/origin/master ' +test_expect_success 'push with colon-less refspec, no ambiguity' ' + + mk_test heads/master heads/t/master && + git branch -f t/master master && + git push testrepo master && + check_push_result $the_commit heads/master && + check_push_result $the_first_commit heads/t/master + +' + test_expect_success 'push with weak ambiguity (1)' ' mk_test heads/master remotes/origin/master && -- cgit v0.10.2-6-g49f6 From 605b4978a105e2f40a353513f616be7d20f91c15 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Sun, 11 Nov 2007 15:01:48 +0100 Subject: refactor fetch's ref matching to use refname_match() The old rules used by fetch were coded as a series of ifs. The old rules are: 1) match full refname if it starts with "refs/" or matches "HEAD" 2) verify that full refname starts with "refs/" 3) match abbreviated name in "refs/" if it starts with "heads/", "tags/", or "remotes/". 4) match abbreviated name in "refs/heads/" This is replaced by the new rules a) match full refname b) match abbreviated name prefixed with "refs/" c) match abbreviated name prefixed with "refs/heads/" The details of the new rules are different from the old rules. We no longer verify that the full refname starts with "refs/". The new rule (a) matches any full string. The old rules (1) and (2) were stricter. Now, the caller is responsible for using sensible full refnames. This should be the case for the current code. The new rule (b) is less strict than old rule (3). The new rule accepts abbreviated names that start with a non-standard prefix below "refs/". Despite this modifications the new rules should handle all cases as expected. Two tests are added to verify that fetch does not resolve short tags or HEAD in remotes. We may even think about loosening the rules a bit more and unify them with the rev-parse rules. This would be done by replacing ref_ref_fetch_rules with ref_ref_parse_rules. Note, the two new test would break. Signed-off-by: Steffen Prohaska Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index 38d9a28..cb8f3ca 100644 --- a/cache.h +++ b/cache.h @@ -417,6 +417,7 @@ extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules); extern const char *ref_rev_parse_rules[]; +extern const char *ref_fetch_rules[]; extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg); extern int validate_headref(const char *ref); diff --git a/refs.c b/refs.c index fc26a93..6a04a66 100644 --- a/refs.c +++ b/refs.c @@ -653,6 +653,13 @@ const char *ref_rev_parse_rules[] = { NULL }; +const char *ref_fetch_rules[] = { + "%.*s", + "refs/%.*s", + "refs/heads/%.*s", + NULL +}; + int refname_match(const char *abbrev_name, const char *full_name, const char **rules) { const char **p; diff --git a/remote.c b/remote.c index 4085c51..48812a7 100644 --- a/remote.c +++ b/remote.c @@ -417,25 +417,6 @@ int remote_has_url(struct remote *remote, const char *url) return 0; } -/* - * Returns true if, under the matching rules for fetching, name is the - * same as the given full name. - */ -static int ref_matches_abbrev(const char *name, const char *full) -{ - if (!prefixcmp(name, "refs/") || !strcmp(name, "HEAD")) - return !strcmp(name, full); - if (prefixcmp(full, "refs/")) - return 0; - if (!prefixcmp(name, "heads/") || - !prefixcmp(name, "tags/") || - !prefixcmp(name, "remotes/")) - return !strcmp(name, full + 5); - if (prefixcmp(full + 5, "heads/")) - return 0; - return !strcmp(full + 11, name); -} - int remote_find_tracking(struct remote *remote, struct refspec *refspec) { int find_src = refspec->src == NULL; @@ -804,7 +785,7 @@ int branch_merge_matches(struct branch *branch, { if (!branch || i < 0 || i >= branch->merge_nr) return 0; - return ref_matches_abbrev(branch->merge[i]->src, refname); + return refname_match(branch->merge[i]->src, refname, ref_fetch_rules); } static struct ref *get_expanded_map(struct ref *remote_refs, @@ -843,7 +824,7 @@ static struct ref *find_ref_by_name_abbrev(struct ref *refs, const char *name) { struct ref *ref; for (ref = refs; ref; ref = ref->next) { - if (ref_matches_abbrev(name, ref->name)) + if (refname_match(name, ref->name, ref_fetch_rules)) return ref; } return NULL; diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index aad863d..2025742 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -95,6 +95,31 @@ test_expect_success 'fetch following tags' ' ' +test_expect_failure 'fetch must not resolve short tag name' ' + + cd "$D" && + + mkdir five && + cd five && + git init && + + git fetch .. anno:five + +' + +test_expect_failure 'fetch must not resolve short remote name' ' + + cd "$D" && + git-update-ref refs/remotes/six/HEAD HEAD + + mkdir six && + cd six && + git init && + + git fetch .. six:six + +' + test_expect_success 'create bundle 1' ' cd "$D" && echo >file updated again by origin && -- cgit v0.10.2-6-g49f6 From 7c0ab4458994aa895855abc4a504cf693ecc0cf1 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Thu, 22 Nov 2007 01:02:52 +0100 Subject: Teach builtin-add to pass multiple paths to git-add--interactive Instead of just accepting a single file parameter, git-add now accepts any number of path parameters, fowarding them to git-add--interactive. Signed-off-by: Wincent Colaiuta Signed-off-by: Junio C Hamano diff --git a/builtin-add.c b/builtin-add.c index cf815a0..9e3beaf 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -135,11 +135,17 @@ static void refresh(int verbose, const char **pathspec) free(seen); } -int interactive_add(void) +int interactive_add(int argc, const char **argv) { - const char *argv[2] = { "add--interactive", NULL }; - - return run_command_v_opt(argv, RUN_GIT_CMD); + int status; + const char **args = xmalloc(sizeof(const char *) * (argc + 1)); + args[0] = "add--interactive"; + memcpy((void *)args + sizeof(const char *), argv, sizeof(const char *) * argc); + args[argc + 1] = NULL; + + status = run_command_v_opt(args, RUN_GIT_CMD); + free(args); + return status; } static struct lock_file lock_file; @@ -163,17 +169,14 @@ static struct option builtin_add_options[] = { int cmd_add(int argc, const char **argv, const char *prefix) { - int i, newfd, orig_argc = argc; + int i, newfd; const char **pathspec; struct dir_struct dir; argc = parse_options(argc, argv, builtin_add_options, builtin_add_usage, 0); - if (add_interactive) { - if (add_interactive != 1 || orig_argc != 2) - die("add --interactive does not take any parameters"); - exit(interactive_add()); - } + if (add_interactive) + exit(interactive_add(argc, argv)); git_config(git_default_config); diff --git a/commit.h b/commit.h index aa67986..d82b8bc 100644 --- a/commit.h +++ b/commit.h @@ -113,7 +113,7 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads, int in_merge_bases(struct commit *, struct commit **, int); -extern int interactive_add(void); +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); -- cgit v0.10.2-6-g49f6 From 4c8416847aa48e2bd60fa26585e32940a1a9c61c Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Thu, 22 Nov 2007 02:36:24 +0100 Subject: Add path-limiting to git-add--interactive Implement Junio's suggestion that git-add--interactive should reproduce the path-limiting semantics of non-interactive git-add. In otherwords, if "git add -i" (unrestricted) shows paths from a set A, "git add -i paths..." should show paths from a subset of the set A and that subset should be defined with the existing ls-files pathspec semantics. Signed-off-by: Wincent Colaiuta Signed-off-by: Junio C Hamano diff --git a/git-add--interactive.perl b/git-add--interactive.perl index fb1e92a..a0e480e 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -37,7 +37,7 @@ sub list_untracked { chomp $_; $_; } - run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @_); + run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV); } my $status_fmt = '%12s %12s %s'; @@ -56,9 +56,17 @@ sub list_modified { my ($only) = @_; my (%data, @return); my ($add, $del, $adddel, $file); + my @tracked = (); + + if (@ARGV) { + @tracked = map { + chomp $_; $_; + } run_cmd_pipe(qw(git ls-files --exclude-standard --), @ARGV); + return if (!@tracked); + } for (run_cmd_pipe(qw(git diff-index --cached - --numstat --summary HEAD))) { + --numstat --summary HEAD --), @tracked)) { if (($add, $del, $file) = /^([-\d]+) ([-\d]+) (.*)/) { my ($change, $bin); @@ -81,7 +89,7 @@ sub list_modified { } } - for (run_cmd_pipe(qw(git diff-files --numstat --summary))) { + for (run_cmd_pipe(qw(git diff-files --numstat --summary --), @tracked)) { if (($add, $del, $file) = /^([-\d]+) ([-\d]+) (.*)/) { if (!exists $data{$file}) { -- cgit v0.10.2-6-g49f6 From 208320de8044acea5f637dd64475c3756d899094 Mon Sep 17 00:00:00 2001 From: Michele Ballabio Date: Thu, 22 Nov 2007 16:20:08 +0100 Subject: git-gui: fix a typo in lib/commit.tcl Signed-off-by: Michele Ballabio Signed-off-by: Shawn O. Pearce diff --git a/lib/commit.tcl b/lib/commit.tcl index 10b0430..b2d2d53 100644 --- a/lib/commit.tcl +++ b/lib/commit.tcl @@ -184,7 +184,7 @@ You must stage at least 1 file before you can commit. A good commit message has the following format: -- First line: Describe in one sentance what you did. +- First line: Describe in one sentence what you did. - Second line: Blank - Remaining lines: Describe why this change is good. "] -- cgit v0.10.2-6-g49f6 From b53a191901db588fe0a2665d2a740e07de2b3e39 Mon Sep 17 00:00:00 2001 From: Michele Ballabio Date: Thu, 22 Nov 2007 11:46:21 +0100 Subject: git-gui: update it.po and glossary/it.po Updates and fixes: * current -> attuale * failed -> impossibile/non riuscito * corrupt -> danneggiato Signed-off-by: Michele Ballabio Signed-off-by: Shawn O. Pearce diff --git a/po/glossary/it.po b/po/glossary/it.po index 8e3d9a2..bb46b48 100644 --- a/po/glossary/it.po +++ b/po/glossary/it.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: git-gui glossary\n" -"POT-Creation-Date: 2007-10-05 22:30+0200\n" +"POT-Creation-Date: 2007-10-19 21:43+0200\n" "PO-Revision-Date: 2007-10-10 15:24+0200\n" "Last-Translator: Michele Ballabio \n" "Language-Team: Italian \n" @@ -50,8 +50,8 @@ msgstr "attivazione, checkout, revisione attiva, prelievo (TortoiseCVS)?" #. "The action of updating the working tree to a revision which was stored in the object database." msgid "checkout [verb]" msgstr "" -"attivare, effettuare un checkout, attivare revisione, prelevare (TortoiseCVS), " -"ritirare (TSVN)?" +"attivare, effettuare un checkout, attivare revisione, prelevare " +"(TortoiseCVS), ritirare (TSVN)?" #. "" msgid "clone [verb]" @@ -99,7 +99,7 @@ msgstr "effettuare la fusione, unire, fondere, eseguire la fusione" msgid "message" msgstr "messaggio, commento" -#. "" +#. "Deletes all stale tracking branches under . These stale branches have already been removed from the remote repository referenced by , but are still locally available in 'remotes/'." msgid "prune" msgstr "potatura" @@ -117,6 +117,10 @@ msgstr "propaga" msgid "redo" msgstr "ripeti, rifai" +#. "An other repository ('remote'). One might have a set of remotes whose branches one tracks." +msgid "remote" +msgstr "remoto" + #. "A collection of refs (?) together with an object database containing all objects which are reachable from the refs... (oops, you've lost me here. Again, please an explanation for mere mortals?)" msgid "repository" msgstr "archivio, repository, database? deposito (rapidsvn)?" @@ -160,8 +164,8 @@ msgstr "etichettare" #. "A regular git branch that is used to follow changes from another repository." msgid "tracking branch" msgstr "" -"duplicato locale di ramo remoto, ramo in 'tracking', ramo inseguitore? ramo di {inseguimento,allineamento," -"rilevamento,puntamento}?" +"duplicato locale di ramo remoto, ramo in 'tracking', ramo inseguitore? ramo " +"di {inseguimento,allineamento,rilevamento,puntamento}?" #. "" msgid "undo" diff --git a/po/it.po b/po/it.po index 7668414..33a8399 100644 --- a/po/it.po +++ b/po/it.po @@ -9,41 +9,41 @@ msgid "" msgstr "" "Project-Id-Version: git-gui\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-10-10 14:42+0200\n" -"PO-Revision-Date: 2007-10-10 15:27+0200\n" -"Last-Translator: Paolo Ciarrocchi \n" +"POT-Creation-Date: 2007-11-09 11:18+0100\n" +"PO-Revision-Date: 2007-11-01 21:05+0100\n" +"Last-Translator: Michele Ballabio \n" "Language-Team: Italian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: git-gui.sh:41 git-gui.sh:634 git-gui.sh:648 git-gui.sh:661 git-gui.sh:744 -#: git-gui.sh:763 +#: git-gui.sh:41 git-gui.sh:604 git-gui.sh:618 git-gui.sh:631 git-gui.sh:714 +#: git-gui.sh:733 msgid "git-gui: fatal error" msgstr "git-gui: errore grave" -#: git-gui.sh:595 +#: git-gui.sh:565 #, tcl-format msgid "Invalid font specified in %s:" msgstr "Caratteri non validi specificati in %s:" -#: git-gui.sh:620 +#: git-gui.sh:590 msgid "Main Font" msgstr "Caratteri principali" -#: git-gui.sh:621 +#: git-gui.sh:591 msgid "Diff/Console Font" msgstr "Caratteri per confronti e terminale" -#: git-gui.sh:635 +#: git-gui.sh:605 msgid "Cannot find git in PATH." msgstr "Impossibile trovare git nel PATH" -#: git-gui.sh:662 +#: git-gui.sh:632 msgid "Cannot parse Git version string:" msgstr "Impossibile determinare la versione di Git:" -#: git-gui.sh:680 +#: git-gui.sh:650 #, tcl-format msgid "" "Git version cannot be determined.\n" @@ -54,7 +54,7 @@ msgid "" "\n" "Assume '%s' is version 1.5.0?\n" msgstr "" -"La versione di GIT non può essere determinata.\n" +"La versione di Git non può essere determinata.\n" "\n" "%s riporta che la versione è '%s'.\n" "\n" @@ -62,79 +62,79 @@ msgstr "" "\n" "Assumere che '%s' sia alla versione 1.5.0?\n" -#: git-gui.sh:853 +#: git-gui.sh:888 msgid "Git directory not found:" msgstr "Non trovo la directory di git: " -#: git-gui.sh:860 +#: git-gui.sh:895 msgid "Cannot move to top of working directory:" msgstr "Impossibile spostarsi sulla directory principale del progetto:" -#: git-gui.sh:867 +#: git-gui.sh:902 msgid "Cannot use funny .git directory:" msgstr "Impossibile usare una .git directory strana:" -#: git-gui.sh:872 +#: git-gui.sh:907 msgid "No working directory" msgstr "Nessuna directory di lavoro" -#: git-gui.sh:1019 +#: git-gui.sh:1054 msgid "Refreshing file status..." msgstr "Controllo dello stato dei file in corso..." -#: git-gui.sh:1084 +#: git-gui.sh:1119 msgid "Scanning for modified files ..." msgstr "Ricerca di file modificati in corso..." -#: git-gui.sh:1259 lib/browser.tcl:245 +#: git-gui.sh:1294 lib/browser.tcl:245 msgid "Ready." msgstr "Pronto." -#: git-gui.sh:1525 +#: git-gui.sh:1560 msgid "Unmodified" msgstr "Non modificato" -#: git-gui.sh:1527 +#: git-gui.sh:1562 msgid "Modified, not staged" msgstr "Modificato, non preparato per una nuova revisione" -#: git-gui.sh:1528 git-gui.sh:1533 +#: git-gui.sh:1563 git-gui.sh:1568 msgid "Staged for commit" msgstr "Preparato per una nuova revisione" -#: git-gui.sh:1529 git-gui.sh:1534 +#: git-gui.sh:1564 git-gui.sh:1569 msgid "Portions staged for commit" msgstr "Parti preparate per una nuova revisione" -#: git-gui.sh:1530 git-gui.sh:1535 +#: git-gui.sh:1565 git-gui.sh:1570 msgid "Staged for commit, missing" msgstr "Preparato per una nuova revisione, mancante" -#: git-gui.sh:1532 +#: git-gui.sh:1567 msgid "Untracked, not staged" msgstr "Non tracciato, non preparato per una nuova revisione" -#: git-gui.sh:1537 +#: git-gui.sh:1572 msgid "Missing" msgstr "Mancante" -#: git-gui.sh:1538 +#: git-gui.sh:1573 msgid "Staged for removal" msgstr "Preparato per la rimozione" -#: git-gui.sh:1539 +#: git-gui.sh:1574 msgid "Staged for removal, still present" msgstr "Preparato alla rimozione, ancora presente" -#: git-gui.sh:1541 git-gui.sh:1542 git-gui.sh:1543 git-gui.sh:1544 +#: git-gui.sh:1576 git-gui.sh:1577 git-gui.sh:1578 git-gui.sh:1579 msgid "Requires merge resolution" msgstr "Richiede risoluzione dei conflitti" -#: git-gui.sh:1579 +#: git-gui.sh:1614 msgid "Starting gitk... please wait..." msgstr "Avvio di gitk... attendere..." -#: git-gui.sh:1588 +#: git-gui.sh:1623 #, tcl-format msgid "" "Unable to start gitk:\n" @@ -145,297 +145,297 @@ msgstr "" "\n" "%s non esiste" -#: git-gui.sh:1788 lib/choose_repository.tcl:32 +#: git-gui.sh:1823 lib/choose_repository.tcl:35 msgid "Repository" msgstr "Archivio" -#: git-gui.sh:1789 +#: git-gui.sh:1824 msgid "Edit" msgstr "Modifica" -#: git-gui.sh:1791 lib/choose_rev.tcl:560 +#: git-gui.sh:1826 lib/choose_rev.tcl:560 msgid "Branch" msgstr "Ramo" -#: git-gui.sh:1794 lib/choose_rev.tcl:547 +#: git-gui.sh:1829 lib/choose_rev.tcl:547 msgid "Commit@@noun" msgstr "Revisione" -#: git-gui.sh:1797 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168 +#: git-gui.sh:1832 lib/merge.tcl:121 lib/merge.tcl:150 lib/merge.tcl:168 msgid "Merge" msgstr "Fusione (Merge)" -#: git-gui.sh:1798 lib/choose_rev.tcl:556 +#: git-gui.sh:1833 lib/choose_rev.tcl:556 msgid "Remote" msgstr "Remoto" -#: git-gui.sh:1807 +#: git-gui.sh:1842 msgid "Browse Current Branch's Files" -msgstr "Esplora i file del ramo corrente" +msgstr "Esplora i file del ramo attuale" -#: git-gui.sh:1811 +#: git-gui.sh:1846 msgid "Browse Branch Files..." msgstr "Esplora i file del ramo..." -#: git-gui.sh:1816 +#: git-gui.sh:1851 msgid "Visualize Current Branch's History" -msgstr "Visualizza la cronologia del ramo corrente" +msgstr "Visualizza la cronologia del ramo attuale" -#: git-gui.sh:1820 +#: git-gui.sh:1855 msgid "Visualize All Branch History" msgstr "Visualizza la cronologia di tutti i rami" -#: git-gui.sh:1827 +#: git-gui.sh:1862 #, tcl-format msgid "Browse %s's Files" msgstr "Esplora i file di %s" -#: git-gui.sh:1829 +#: git-gui.sh:1864 #, tcl-format msgid "Visualize %s's History" msgstr "Visualizza la cronologia di %s" -#: git-gui.sh:1834 lib/database.tcl:27 lib/database.tcl:67 +#: git-gui.sh:1869 lib/database.tcl:27 lib/database.tcl:67 msgid "Database Statistics" msgstr "Statistiche dell'archivio" -#: git-gui.sh:1837 lib/database.tcl:34 +#: git-gui.sh:1872 lib/database.tcl:34 msgid "Compress Database" msgstr "Comprimi l'archivio" -#: git-gui.sh:1840 +#: git-gui.sh:1875 msgid "Verify Database" msgstr "Verifica l'archivio" -#: git-gui.sh:1847 git-gui.sh:1851 git-gui.sh:1855 lib/shortcut.tcl:9 -#: lib/shortcut.tcl:45 lib/shortcut.tcl:84 +#: git-gui.sh:1882 git-gui.sh:1886 git-gui.sh:1890 lib/shortcut.tcl:7 +#: lib/shortcut.tcl:39 lib/shortcut.tcl:71 msgid "Create Desktop Icon" msgstr "Crea icona desktop" -#: git-gui.sh:1860 lib/choose_repository.tcl:36 lib/choose_repository.tcl:95 +#: git-gui.sh:1895 lib/choose_repository.tcl:176 lib/choose_repository.tcl:184 msgid "Quit" msgstr "Esci" -#: git-gui.sh:1867 +#: git-gui.sh:1902 msgid "Undo" msgstr "Annulla" -#: git-gui.sh:1870 +#: git-gui.sh:1905 msgid "Redo" msgstr "Ripeti" -#: git-gui.sh:1874 git-gui.sh:2366 +#: git-gui.sh:1909 git-gui.sh:2403 msgid "Cut" msgstr "Taglia" -#: git-gui.sh:1877 git-gui.sh:2369 git-gui.sh:2440 git-gui.sh:2512 +#: git-gui.sh:1912 git-gui.sh:2406 git-gui.sh:2477 git-gui.sh:2549 #: lib/console.tcl:67 msgid "Copy" msgstr "Copia" -#: git-gui.sh:1880 git-gui.sh:2372 +#: git-gui.sh:1915 git-gui.sh:2409 msgid "Paste" msgstr "Incolla" -#: git-gui.sh:1883 git-gui.sh:2375 lib/branch_delete.tcl:26 +#: git-gui.sh:1918 git-gui.sh:2412 lib/branch_delete.tcl:26 #: lib/remote_branch_delete.tcl:38 msgid "Delete" msgstr "Elimina" -#: git-gui.sh:1887 git-gui.sh:2379 git-gui.sh:2516 lib/console.tcl:69 +#: git-gui.sh:1922 git-gui.sh:2416 git-gui.sh:2553 lib/console.tcl:69 msgid "Select All" msgstr "Seleziona tutto" -#: git-gui.sh:1896 +#: git-gui.sh:1931 msgid "Create..." msgstr "Crea..." -#: git-gui.sh:1902 +#: git-gui.sh:1937 msgid "Checkout..." msgstr "Attiva..." -#: git-gui.sh:1908 +#: git-gui.sh:1943 msgid "Rename..." msgstr "Rinomina" -#: git-gui.sh:1913 git-gui.sh:2012 +#: git-gui.sh:1948 git-gui.sh:2048 msgid "Delete..." msgstr "Elimina..." -#: git-gui.sh:1918 +#: git-gui.sh:1953 msgid "Reset..." msgstr "Ripristina..." -#: git-gui.sh:1930 git-gui.sh:2313 +#: git-gui.sh:1965 git-gui.sh:2350 msgid "New Commit" msgstr "Nuova revisione" -#: git-gui.sh:1938 git-gui.sh:2320 +#: git-gui.sh:1973 git-gui.sh:2357 msgid "Amend Last Commit" msgstr "Correggi l'ultima revisione" -#: git-gui.sh:1947 git-gui.sh:2280 lib/remote_branch_delete.tcl:99 +#: git-gui.sh:1982 git-gui.sh:2317 lib/remote_branch_delete.tcl:99 msgid "Rescan" msgstr "Analizza nuovamente" -#: git-gui.sh:1953 +#: git-gui.sh:1988 msgid "Stage To Commit" msgstr "Prepara per una nuova revisione" -#: git-gui.sh:1958 +#: git-gui.sh:1994 msgid "Stage Changed Files To Commit" msgstr "Prepara i file modificati per una nuova revisione" -#: git-gui.sh:1964 +#: git-gui.sh:2000 msgid "Unstage From Commit" msgstr "Annulla preparazione" -#: git-gui.sh:1969 lib/index.tcl:352 +#: git-gui.sh:2005 lib/index.tcl:393 msgid "Revert Changes" msgstr "Annulla modifiche" -#: git-gui.sh:1976 git-gui.sh:2292 git-gui.sh:2390 +#: git-gui.sh:2012 git-gui.sh:2329 git-gui.sh:2427 msgid "Sign Off" msgstr "Sign Off" -#: git-gui.sh:1980 git-gui.sh:2296 +#: git-gui.sh:2016 git-gui.sh:2333 msgid "Commit@@verb" msgstr "Nuova revisione" -#: git-gui.sh:1991 +#: git-gui.sh:2027 msgid "Local Merge..." msgstr "Fusione locale..." -#: git-gui.sh:1996 +#: git-gui.sh:2032 msgid "Abort Merge..." msgstr "Interrompi fusione..." -#: git-gui.sh:2008 +#: git-gui.sh:2044 msgid "Push..." msgstr "Propaga..." -#: git-gui.sh:2019 lib/choose_repository.tcl:41 +#: git-gui.sh:2055 lib/choose_repository.tcl:40 msgid "Apple" msgstr "Apple" -#: git-gui.sh:2022 git-gui.sh:2044 lib/about.tcl:13 -#: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50 +#: git-gui.sh:2058 git-gui.sh:2080 lib/about.tcl:13 +#: lib/choose_repository.tcl:43 lib/choose_repository.tcl:49 #, tcl-format msgid "About %s" msgstr "Informazioni su %s" -#: git-gui.sh:2026 +#: git-gui.sh:2062 msgid "Preferences..." msgstr "Preferenze..." -#: git-gui.sh:2034 git-gui.sh:2558 +#: git-gui.sh:2070 git-gui.sh:2595 msgid "Options..." msgstr "Opzioni..." -#: git-gui.sh:2040 lib/choose_repository.tcl:47 +#: git-gui.sh:2076 lib/choose_repository.tcl:46 msgid "Help" msgstr "Aiuto" -#: git-gui.sh:2081 +#: git-gui.sh:2117 msgid "Online Documentation" msgstr "Documentazione sul web" -#: git-gui.sh:2165 +#: git-gui.sh:2201 #, tcl-format msgid "fatal: cannot stat path %s: No such file or directory" msgstr "" "errore grave: impossibile effettuare lo stat del path %s: file o directory " "non trovata" -#: git-gui.sh:2198 +#: git-gui.sh:2234 msgid "Current Branch:" msgstr "Ramo attuale:" -#: git-gui.sh:2219 +#: git-gui.sh:2255 msgid "Staged Changes (Will Commit)" msgstr "Modifiche preparate (saranno nella nuova revisione)" -#: git-gui.sh:2239 +#: git-gui.sh:2274 msgid "Unstaged Changes" msgstr "Modifiche non preparate" -#: git-gui.sh:2286 +#: git-gui.sh:2323 msgid "Stage Changed" msgstr "Prepara modificati" -#: git-gui.sh:2302 lib/transport.tcl:93 lib/transport.tcl:182 +#: git-gui.sh:2339 lib/transport.tcl:93 lib/transport.tcl:182 msgid "Push" msgstr "Propaga (Push)" -#: git-gui.sh:2332 +#: git-gui.sh:2369 msgid "Initial Commit Message:" msgstr "Messaggio di revisione iniziale:" -#: git-gui.sh:2333 +#: git-gui.sh:2370 msgid "Amended Commit Message:" msgstr "Messaggio di revisione corretto:" -#: git-gui.sh:2334 +#: git-gui.sh:2371 msgid "Amended Initial Commit Message:" msgstr "Messaggio iniziale di revisione corretto:" -#: git-gui.sh:2335 +#: git-gui.sh:2372 msgid "Amended Merge Commit Message:" msgstr "Messaggio di fusione corretto:" -#: git-gui.sh:2336 +#: git-gui.sh:2373 msgid "Merge Commit Message:" msgstr "Messaggio di fusione:" -#: git-gui.sh:2337 +#: git-gui.sh:2374 msgid "Commit Message:" msgstr "Messaggio di revisione:" -#: git-gui.sh:2382 git-gui.sh:2520 lib/console.tcl:71 +#: git-gui.sh:2419 git-gui.sh:2557 lib/console.tcl:71 msgid "Copy All" msgstr "Copia tutto" -#: git-gui.sh:2406 lib/blame.tcl:104 +#: git-gui.sh:2443 lib/blame.tcl:104 msgid "File:" msgstr "File:" -#: git-gui.sh:2508 +#: git-gui.sh:2545 msgid "Refresh" msgstr "Rinfresca" -#: git-gui.sh:2529 +#: git-gui.sh:2566 msgid "Apply/Reverse Hunk" msgstr "Applica/Inverti sezione" -#: git-gui.sh:2535 +#: git-gui.sh:2572 msgid "Decrease Font Size" msgstr "Diminuisci dimensione caratteri" -#: git-gui.sh:2539 +#: git-gui.sh:2576 msgid "Increase Font Size" msgstr "Aumenta dimensione caratteri" -#: git-gui.sh:2544 +#: git-gui.sh:2581 msgid "Show Less Context" msgstr "Mostra meno contesto" -#: git-gui.sh:2551 +#: git-gui.sh:2588 msgid "Show More Context" msgstr "Mostra più contesto" -#: git-gui.sh:2565 +#: git-gui.sh:2602 msgid "Unstage Hunk From Commit" msgstr "Sezione non preparata per una nuova revisione" -#: git-gui.sh:2567 +#: git-gui.sh:2604 msgid "Stage Hunk For Commit" msgstr "Prepara sezione per una nuova revisione" -#: git-gui.sh:2586 +#: git-gui.sh:2623 msgid "Initializing..." msgstr "Inizializzazione..." -#: git-gui.sh:2677 +#: git-gui.sh:2718 #, tcl-format msgid "" "Possible environment issues exist.\n" @@ -452,7 +452,7 @@ msgstr "" "da %s:\n" "\n" -#: git-gui.sh:2707 +#: git-gui.sh:2748 msgid "" "\n" "This is due to a known issue with the\n" @@ -462,7 +462,7 @@ msgstr "" "Ciò è dovuto a un problema conosciuto\n" "causato dall'eseguibile Tcl distribuito da Cygwin." -#: git-gui.sh:2712 +#: git-gui.sh:2753 #, tcl-format msgid "" "\n" @@ -582,7 +582,7 @@ msgstr "Crea ramo" msgid "Create New Branch" msgstr "Crea nuovo ramo" -#: lib/branch_create.tcl:31 lib/choose_repository.tcl:199 +#: lib/branch_create.tcl:31 lib/choose_repository.tcl:375 msgid "Create" msgstr "Crea" @@ -682,7 +682,7 @@ msgid "" "Failed to delete branches:\n" "%s" msgstr "" -"Cancellazione rami fallita:\n" +"Impossibile cancellare i rami:\n" "%s" #: lib/branch_rename.tcl:14 lib/branch_rename.tcl:22 @@ -736,9 +736,9 @@ msgstr "[Directory superiore]" msgid "Browse Branch Files" msgstr "Esplora i file del ramo" -#: lib/browser.tcl:277 lib/choose_repository.tcl:215 -#: lib/choose_repository.tcl:305 lib/choose_repository.tcl:315 -#: lib/choose_repository.tcl:811 +#: lib/browser.tcl:277 lib/choose_repository.tcl:391 +#: lib/choose_repository.tcl:482 lib/choose_repository.tcl:492 +#: lib/choose_repository.tcl:989 msgid "Browse" msgstr "Sfoglia" @@ -782,11 +782,12 @@ msgstr "La strategia di fusione '%s' non è supportata." #: lib/checkout_op.tcl:239 #, tcl-format msgid "Failed to update '%s'." -msgstr "Aggiornamento di '%s' fallito." +msgstr "Impossibile aggiornare '%s'." #: lib/checkout_op.tcl:251 msgid "Staging area (index) is already locked." -msgstr "L'area di preparazione per una nuova revisione (indice) è già bloccata." +msgstr "" +"L'area di preparazione per una nuova revisione (indice) è già bloccata." #: lib/checkout_op.tcl:266 msgid "" @@ -801,7 +802,7 @@ msgstr "" "\n" "Un altro programma Git ha modificato questo archivio dall'ultima analisi. " "Bisogna effettuare una nuova analisi prima di poter cambiare il ramo " -"corrente.\n" +"attuale.\n" "\n" "La nuova analisi comincerà ora.\n" @@ -870,7 +871,7 @@ msgid "" "\n" "This should not have occurred. %s will now close and give up." msgstr "" -"Preparazione ramo corrente fallita.\n" +"Impossibile preparare il ramo attuale.\n" "\n" "Questa directory di lavoro è stata convertita solo parzialmente. I file sono " "stati aggiornati correttamente, ma l'aggiornamento di un file di Git ha " @@ -902,214 +903,230 @@ msgstr "" "Questo è un testo d'esempio.\n" "Se ti piace questo testo, può essere il carattere giusto." -#: lib/choose_repository.tcl:25 +#: lib/choose_repository.tcl:27 msgid "Git Gui" msgstr "Git Gui" -#: lib/choose_repository.tcl:69 lib/choose_repository.tcl:204 +#: lib/choose_repository.tcl:80 lib/choose_repository.tcl:380 msgid "Create New Repository" msgstr "Crea nuovo archivio" -#: lib/choose_repository.tcl:74 lib/choose_repository.tcl:291 +#: lib/choose_repository.tcl:86 +msgid "New..." +msgstr "Nuovo..." + +#: lib/choose_repository.tcl:93 lib/choose_repository.tcl:468 msgid "Clone Existing Repository" msgstr "Clona archivio esistente" -#: lib/choose_repository.tcl:79 lib/choose_repository.tcl:800 +#: lib/choose_repository.tcl:99 +msgid "Clone..." +msgstr "Clona..." + +#: lib/choose_repository.tcl:106 lib/choose_repository.tcl:978 msgid "Open Existing Repository" msgstr "Apri archivio esistente" -#: lib/choose_repository.tcl:91 -msgid "Next >" -msgstr "Successivo >" +#: lib/choose_repository.tcl:112 +msgid "Open..." +msgstr "Apri..." + +#: lib/choose_repository.tcl:125 +msgid "Recent Repositories" +msgstr "Archivi recenti" + +#: lib/choose_repository.tcl:131 +msgid "Open Recent Repository:" +msgstr "Apri archivio recente:" -#: lib/choose_repository.tcl:152 +#: lib/choose_repository.tcl:294 #, tcl-format msgid "Location %s already exists." msgstr "La posizione %s esiste già." -#: lib/choose_repository.tcl:158 lib/choose_repository.tcl:165 -#: lib/choose_repository.tcl:172 +#: lib/choose_repository.tcl:300 lib/choose_repository.tcl:307 +#: lib/choose_repository.tcl:314 #, tcl-format msgid "Failed to create repository %s:" msgstr "Impossibile creare l'archivio %s:" -#: lib/choose_repository.tcl:209 lib/choose_repository.tcl:309 +#: lib/choose_repository.tcl:385 lib/choose_repository.tcl:486 msgid "Directory:" msgstr "Directory:" -#: lib/choose_repository.tcl:238 lib/choose_repository.tcl:363 -#: lib/choose_repository.tcl:834 +#: lib/choose_repository.tcl:415 lib/choose_repository.tcl:544 +#: lib/choose_repository.tcl:1013 msgid "Git Repository" msgstr "Archivio Git" -#: lib/choose_repository.tcl:253 lib/choose_repository.tcl:260 +#: lib/choose_repository.tcl:430 lib/choose_repository.tcl:437 #, tcl-format msgid "Directory %s already exists." msgstr "La directory %s esiste già." -#: lib/choose_repository.tcl:265 +#: lib/choose_repository.tcl:442 #, tcl-format msgid "File %s already exists." msgstr "Il file %s esiste già." -#: lib/choose_repository.tcl:286 +#: lib/choose_repository.tcl:463 msgid "Clone" msgstr "Clona" -#: lib/choose_repository.tcl:299 +#: lib/choose_repository.tcl:476 msgid "URL:" msgstr "URL:" -#: lib/choose_repository.tcl:319 +#: lib/choose_repository.tcl:496 msgid "Clone Type:" msgstr "Tipo di clone:" -#: lib/choose_repository.tcl:325 +#: lib/choose_repository.tcl:502 msgid "Standard (Fast, Semi-Redundant, Hardlinks)" msgstr "Standard (veloce, semi-ridondante, con hardlink)" -#: lib/choose_repository.tcl:331 +#: lib/choose_repository.tcl:508 msgid "Full Copy (Slower, Redundant Backup)" msgstr "Copia completa (più lento, backup ridondante)" -#: lib/choose_repository.tcl:337 +#: lib/choose_repository.tcl:514 msgid "Shared (Fastest, Not Recommended, No Backup)" msgstr "Shared (il più veloce, non raccomandato, nessun backup)" -#: lib/choose_repository.tcl:369 lib/choose_repository.tcl:418 -#: lib/choose_repository.tcl:560 lib/choose_repository.tcl:630 -#: lib/choose_repository.tcl:840 lib/choose_repository.tcl:848 +#: lib/choose_repository.tcl:550 lib/choose_repository.tcl:597 +#: lib/choose_repository.tcl:738 lib/choose_repository.tcl:808 +#: lib/choose_repository.tcl:1019 lib/choose_repository.tcl:1027 #, tcl-format msgid "Not a Git repository: %s" msgstr "%s non è un archivio Git." -#: lib/choose_repository.tcl:405 +#: lib/choose_repository.tcl:586 msgid "Standard only available for local repository." msgstr "Standard è disponibile solo per archivi locali." -#: lib/choose_repository.tcl:409 +#: lib/choose_repository.tcl:590 msgid "Shared only available for local repository." msgstr "Shared è disponibile solo per archivi locali." -#: lib/choose_repository.tcl:439 +#: lib/choose_repository.tcl:617 msgid "Failed to configure origin" msgstr "Impossibile configurare origin" -#: lib/choose_repository.tcl:451 +#: lib/choose_repository.tcl:629 msgid "Counting objects" msgstr "Calcolo oggetti" -#: lib/choose_repository.tcl:452 +#: lib/choose_repository.tcl:630 msgid "buckets" msgstr "" -#: lib/choose_repository.tcl:476 +#: lib/choose_repository.tcl:654 #, tcl-format msgid "Unable to copy objects/info/alternates: %s" msgstr "Impossibile copiare oggetti/info/alternate: %s" -#: lib/choose_repository.tcl:512 +#: lib/choose_repository.tcl:690 #, tcl-format msgid "Nothing to clone from %s." msgstr "Niente da clonare da %s." -#: lib/choose_repository.tcl:514 lib/choose_repository.tcl:728 -#: lib/choose_repository.tcl:740 +#: lib/choose_repository.tcl:692 lib/choose_repository.tcl:906 +#: lib/choose_repository.tcl:918 msgid "The 'master' branch has not been initialized." msgstr "Il ramo 'master' non è stato inizializzato." -#: lib/choose_repository.tcl:527 +#: lib/choose_repository.tcl:705 msgid "Hardlinks are unavailable. Falling back to copying." msgstr "Impossibile utilizzare gli hardlink. Si ricorrerà alla copia." -#: lib/choose_repository.tcl:539 +#: lib/choose_repository.tcl:717 #, tcl-format msgid "Cloning from %s" msgstr "Clonazione da %s" -#: lib/choose_repository.tcl:570 +#: lib/choose_repository.tcl:748 msgid "Copying objects" msgstr "Copia degli oggetti" -#: lib/choose_repository.tcl:571 +#: lib/choose_repository.tcl:749 msgid "KiB" msgstr "KiB" -#: lib/choose_repository.tcl:595 +#: lib/choose_repository.tcl:773 #, tcl-format msgid "Unable to copy object: %s" msgstr "Impossibile copiare oggetto: %s" -#: lib/choose_repository.tcl:605 +#: lib/choose_repository.tcl:783 msgid "Linking objects" msgstr "Collegamento oggetti" -#: lib/choose_repository.tcl:606 +#: lib/choose_repository.tcl:784 msgid "objects" msgstr "oggetti" -#: lib/choose_repository.tcl:614 +#: lib/choose_repository.tcl:792 #, tcl-format msgid "Unable to hardlink object: %s" msgstr "Hardlink impossibile sull'oggetto: %s" -#: lib/choose_repository.tcl:669 +#: lib/choose_repository.tcl:847 msgid "Cannot fetch branches and objects. See console output for details." msgstr "" "Impossibile recuperare rami e oggetti. Controllare i dettagli forniti dalla " "console." -#: lib/choose_repository.tcl:680 +#: lib/choose_repository.tcl:858 msgid "Cannot fetch tags. See console output for details." msgstr "" "Impossibile recuperare le etichette. Controllare i dettagli forniti dalla " "console." -#: lib/choose_repository.tcl:704 +#: lib/choose_repository.tcl:882 msgid "Cannot determine HEAD. See console output for details." msgstr "" "Impossibile determinare HEAD. Controllare i dettagli forniti dalla console." -#: lib/choose_repository.tcl:713 +#: lib/choose_repository.tcl:891 #, tcl-format msgid "Unable to cleanup %s" msgstr "Impossibile ripulire %s" -#: lib/choose_repository.tcl:719 +#: lib/choose_repository.tcl:897 msgid "Clone failed." -msgstr "Clonazione fallita." +msgstr "Clonazione non riuscita." -#: lib/choose_repository.tcl:726 +#: lib/choose_repository.tcl:904 msgid "No default branch obtained." msgstr "Non è stato trovato un ramo predefinito." -#: lib/choose_repository.tcl:737 +#: lib/choose_repository.tcl:915 #, tcl-format msgid "Cannot resolve %s as a commit." msgstr "Impossibile risolvere %s come una revisione." -#: lib/choose_repository.tcl:749 +#: lib/choose_repository.tcl:927 msgid "Creating working directory" msgstr "Creazione directory di lavoro" -#: lib/choose_repository.tcl:750 lib/index.tcl:15 lib/index.tcl:80 -#: lib/index.tcl:149 +#: lib/choose_repository.tcl:928 lib/index.tcl:65 lib/index.tcl:127 +#: lib/index.tcl:193 msgid "files" msgstr "file" -#: lib/choose_repository.tcl:779 +#: lib/choose_repository.tcl:957 msgid "Initial file checkout failed." -msgstr "Attivazione iniziale impossibile." +msgstr "Attivazione iniziale non riuscita." -#: lib/choose_repository.tcl:795 +#: lib/choose_repository.tcl:973 msgid "Open" msgstr "Apri" -#: lib/choose_repository.tcl:805 +#: lib/choose_repository.tcl:983 msgid "Repository:" msgstr "Archivio:" -#: lib/choose_repository.tcl:854 +#: lib/choose_repository.tcl:1033 #, tcl-format msgid "Failed to open repository %s:" msgstr "Impossibile accedere all'archivio %s:" @@ -1164,8 +1181,8 @@ msgid "" msgstr "" "Non c'è niente da correggere.\n" "\n" -"Stai per creare la revisione iniziale. Non esiste una revisione " -"precedente da correggere.\n" +"Stai per creare la revisione iniziale. Non esiste una revisione precedente " +"da correggere.\n" #: lib/commit.tcl:18 msgid "" @@ -1242,8 +1259,8 @@ msgid "" msgstr "" "Nessuna modifica per la nuova revisione.\n" "\n" -"Devi preparare per una nuova revisione almeno 1 file prima di effettuare questa " -"operazione.\n" +"Devi preparare per una nuova revisione almeno 1 file prima di effettuare " +"questa operazione.\n" #: lib/commit.tcl:183 msgid "" @@ -1265,12 +1282,12 @@ msgstr "" #: lib/commit.tcl:257 msgid "write-tree failed:" -msgstr "write-tree fallito:" +msgstr "write-tree non riuscito:" #: lib/commit.tcl:275 #, tcl-format msgid "Commit %s appears to be corrupt" -msgstr "La revisione %s sembra essere corrotta" +msgstr "La revisione %s sembra essere danneggiata" #: lib/commit.tcl:279 msgid "" @@ -1297,11 +1314,11 @@ msgstr "attenzione: Tcl non supporta la codifica '%s'." #: lib/commit.tcl:317 msgid "commit-tree failed:" -msgstr "commit-tree fallito:" +msgstr "commit-tree non riuscito:" #: lib/commit.tcl:339 msgid "update-ref failed:" -msgstr "update-ref fallito:" +msgstr "update-ref non riuscito:" #: lib/commit.tcl:430 #, tcl-format @@ -1318,7 +1335,7 @@ msgstr "Successo" #: lib/console.tcl:196 msgid "Error: Command Failed" -msgstr "Errore: comando fallito" +msgstr "Errore: comando non riuscito" #: lib/database.tcl:43 msgid "Number of loose objects" @@ -1429,7 +1446,7 @@ msgstr "Errore nel caricamento delle differenze:" #: lib/diff.tcl:302 msgid "Failed to unstage selected hunk." -msgstr "La sezione scelta è ancora pronta per una nuova revisione." +msgstr "Impossibile rimuovere la sezione scelta dalla nuova revisione." #: lib/diff.tcl:309 msgid "Failed to stage selected hunk." @@ -1445,35 +1462,60 @@ msgstr "attenzione" #: lib/error.tcl:81 msgid "You must correct the above errors before committing." -msgstr "Bisogna correggere gli errori suddetti prima di creare una nuova revisione." +msgstr "" +"Bisogna correggere gli errori suddetti prima di creare una nuova revisione." -#: lib/index.tcl:241 +#: lib/index.tcl:6 +msgid "Unable to unlock the index." +msgstr "Impossibile sbloccare l'accesso all'indice" + +#: lib/index.tcl:15 +msgid "Index Error" +msgstr "Errore nell'indice" + +#: lib/index.tcl:21 +msgid "" +"Updating the Git index failed. A rescan will be automatically started to " +"resynchronize git-gui." +msgstr "" +"Impossibile aggiornare l'indice. Ora sarà avviata una nuova analisi che " +"aggiornerà git-gui." + +#: lib/index.tcl:27 +msgid "Continue" +msgstr "Continua" + +#: lib/index.tcl:31 +msgid "Unlock Index" +msgstr "Sblocca l'accesso all'indice" + +#: lib/index.tcl:282 #, tcl-format msgid "Unstaging %s from commit" msgstr "%s non farà parte della prossima revisione" -#: lib/index.tcl:285 +#: lib/index.tcl:326 #, tcl-format msgid "Adding %s" msgstr "Aggiunta di %s in corso" -#: lib/index.tcl:340 +#: lib/index.tcl:381 #, tcl-format msgid "Revert changes in file %s?" msgstr "Annullare le modifiche nel file %s?" -#: lib/index.tcl:342 +#: lib/index.tcl:383 #, tcl-format msgid "Revert changes in these %i files?" msgstr "Annullare le modifiche in questi %i file?" -#: lib/index.tcl:348 +#: lib/index.tcl:389 msgid "Any unstaged changes will be permanently lost by the revert." msgstr "" "Tutte le modifiche non preparate per una nuova revisione saranno perse per " "sempre." -#: lib/index.tcl:351 +#: lib/index.tcl:392 msgid "Do Nothing" msgstr "Non fare niente" @@ -1519,8 +1561,8 @@ msgstr "" "Il file %s ha dei conflitti.\n" "\n" "Bisogna risolvere i conflitti, preparare il file per una nuova revisione ed " -"infine crearla per completare la fusione corrente. Solo a questo punto " -"potrai iniziare un'altra fusione.\n" +"infine crearla per completare la fusione attuale. Solo a questo punto potrai " +"iniziare un'altra fusione.\n" #: lib/merge.tcl:54 #, tcl-format @@ -1536,9 +1578,9 @@ msgstr "" "\n" "Il file %s è stato modificato.\n" "\n" -"Bisogna completare la creazione della revisione corrente prima di iniziare una fusione. " -"In questo modo sarà più facile interrompere una fusione non riuscita, nel " -"caso ce ne fosse bisogno.\n" +"Bisogna completare la creazione della revisione attuale prima di iniziare " +"una fusione. In questo modo sarà più facile interrompere una fusione non " +"riuscita, nel caso ce ne fosse bisogno.\n" #: lib/merge.tcl:106 #, tcl-format @@ -1556,7 +1598,7 @@ msgstr "Fusione completata con successo." #: lib/merge.tcl:133 msgid "Merge failed. Conflict resolution is required." -msgstr "Fusione fallita. Bisogna risolvere i conflitti." +msgstr "Fusione non riuscita. Bisogna risolvere i conflitti." #: lib/merge.tcl:158 #, tcl-format @@ -1587,10 +1629,10 @@ msgid "" msgstr "" "Interrompere fusione?\n" "\n" -"L'interruzione della fusione corrente causerà la perdita di *TUTTE* le " +"L'interruzione della fusione attuale causerà la perdita di *TUTTE* le " "modifiche non ancora presenti nell'archivio.\n" "\n" -"Continuare con l'interruzione della fusione corrente?" +"Continuare con l'interruzione della fusione attuale?" #: lib/merge.tcl:228 msgid "" @@ -1605,7 +1647,7 @@ msgstr "" "L'annullamento delle modifiche causerà la perdita di *TUTTE* le modifiche " "non ancora presenti nell'archivio.\n" "\n" -"Continuare con l'annullamento delle modifiche correnti?" +"Continuare con l'annullamento delle modifiche attuali?" #: lib/merge.tcl:239 msgid "Aborting" @@ -1613,7 +1655,7 @@ msgstr "Interruzione in corso" #: lib/merge.tcl:266 msgid "Abort failed." -msgstr "Interruzione fallita." +msgstr "Interruzione non riuscita." #: lib/merge.tcl:268 msgid "Abort completed. Ready." @@ -1621,7 +1663,7 @@ msgstr "Interruzione completata. Pronto." #: lib/option.tcl:82 msgid "Restore Defaults" -msgstr "Ripristina predefiniti" +msgstr "Ripristina valori predefiniti" #: lib/option.tcl:86 msgid "Save" @@ -1763,8 +1805,8 @@ msgid "" "One or more of the merge tests failed because you have not fetched the " "necessary commits. Try fetching from %s first." msgstr "" -"Una o più verifiche di fusione sono fallite perché mancano le revisioni " -"necessarie. Prova prima a recuperarle da %s." +"Impossibile verificare una o più fusioni: mancano le revisioni necessarie. " +"Prova prima a recuperarle da %s." #: lib/remote_branch_delete.tcl:207 msgid "Please select one or more branches to delete." @@ -1794,18 +1836,18 @@ msgstr "Nessun archivio selezionato." msgid "Scanning %s..." msgstr "Analisi in corso %s..." -#: lib/shortcut.tcl:26 lib/shortcut.tcl:74 -msgid "Cannot write script:" -msgstr "Impossibile scrivere script:" +#: lib/shortcut.tcl:20 lib/shortcut.tcl:61 +msgid "Cannot write shortcut:" +msgstr "Impossibile scrivere shortcut:" -#: lib/shortcut.tcl:149 +#: lib/shortcut.tcl:136 msgid "Cannot write icon:" msgstr "Impossibile scrivere icona:" #: lib/status_bar.tcl:83 #, tcl-format msgid "%s ... %*i of %*i %s (%3i%%)" -msgstr "%s ... %*i di %*i %s (%3i%%)" +msgstr "%1$s ... %6$s: %2$*i di %4$*i (%7$3i%%)" #: lib/transport.tcl:6 #, tcl-format @@ -1869,4 +1911,3 @@ msgstr "Utilizza 'thin pack' (per connessioni lente)" #: lib/transport.tcl:168 msgid "Include tags" msgstr "Includi etichette" - -- cgit v0.10.2-6-g49f6 From d63c2fd192271ea2d85c81edfad90aa42fec26ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Fri, 2 Nov 2007 11:33:06 -0400 Subject: Add testcase for amending and fixing author in git commit. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We used to clobber author time, but we shouldn't. Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index 9dba104..e601028 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -242,4 +242,19 @@ test_expect_success 'multiple -m' ' ' +author="The Real Author " +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_done -- cgit v0.10.2-6-g49f6 From 943316e96ca2dad67086af2f945e42467a27ddd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Fri, 2 Nov 2007 11:33:08 -0400 Subject: Export launch_editor() and make it accept ':' as a no-op editor. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano diff --git a/builtin-tag.c b/builtin-tag.c index cbb0f04..88a5449 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -17,7 +17,7 @@ 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; @@ -42,6 +42,9 @@ static void launch_editor(const char *path, struct strbuf *buffer) if (!editor) editor = "vi"; + if (!strcmp(editor, ":")) + return; + memset(&child, 0, sizeof(child)); child.argv = args; args[0] = editor; diff --git a/strbuf.h b/strbuf.h index 1391912..8334a9b 100644 --- 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 */ -- cgit v0.10.2-6-g49f6 From f5bbc3225c4b073a7ff3218164a0c820299bc9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Thu, 8 Nov 2007 11:59:00 -0500 Subject: Port git commit to C. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes git commit a builtin and moves git-commit.sh to contrib/examples. This also removes the git-runstatus helper, which was mostly just a git-status.sh implementation detail. Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano diff --git a/.gitignore b/.gitignore index c8c13f5..bbd7f55 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Makefile b/Makefile index 7a0ee78..35f9c87 100644 --- 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 \ @@ -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 \ @@ -369,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 \ @@ -838,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)|' \ diff --git a/builtin-commit.c b/builtin-commit.c new file mode 100644 index 0000000..669cc6b --- /dev/null +++ b/builtin-commit.c @@ -0,0 +1,614 @@ +/* + * Builtin "git commit" + * + * Copyright (c) 2007 Kristian Høgsberg + * Based on git-commit.sh by Junio C Hamano and Linus Torvalds + */ + +#include "cache.h" +#include "cache-tree.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" + +static const char * const builtin_commit_usage[] = { + "git-commit [options] [--] ...", + 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 lock_file; + +static char *logfile, *force_author, *message, *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; + +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_STRING('m', "message", &message, "MESSAGE", "specify commit message"), + 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 char *prepare_index(const char **files, const char *prefix) +{ + int fd; + struct tree *tree; + struct lock_file *next_index_lock; + + if (interactive) { + interactive_add(); + return get_index_file(); + } + + fd = hold_locked_index(&lock_file, 1); + if (read_cache() < 0) + die("index file corrupt"); + + if (all || also) { + add_files_to_cache(verbose, also ? prefix : NULL, files); + if (write_cache(fd, active_cache, active_nr) || close(fd)) + die("unable to write new_index file"); + return lock_file.filename; + } + + if (*files == NULL) { + /* Commit index as-is. */ + rollback_lock_file(&lock_file); + return get_index_file(); + } + + /* update the user index file */ + add_files_to_cache(verbose, prefix, files); + if (write_cache(fd, active_cache, active_nr) || close(fd)) + die("unable to write new_index file"); + + 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"); + } + + /* Use a lock file to garbage collect the temporary index file. */ + next_index_lock = xmalloc(sizeof(*next_index_lock)); + fd = hold_lock_file_for_update(next_index_lock, + git_path("next-index-%d", getpid()), 1); + add_files_to_cache(verbose, prefix, files); + if (write_cache(fd, active_cache, active_nr) || close(fd)) + die("unable to write new_index file"); + + return next_index_lock->filename; +} + +static int run_status(FILE *fp, const char *index_file) +{ + struct wt_status s; + + wt_status_prepare(&s); + + 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) +{ + struct stat statbuf; + int commitable; + struct strbuf sb; + char *buffer; + FILE *fp; + + strbuf_init(&sb, 0); + if (message) { + strbuf_add(&sb, message, strlen(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 (fwrite(sb.buf, 1, sb.len, fp) < sb.len) + die("could not write commit template: %s\n", + strerror(errno)); + + if (signoff) { + const char *info, *bol; + + info = git_committer_info(1); + strbuf_addch(&sb, '\0'); + bol = strrchr(sb.buf + sb.len - 1, '\n'); + if (!bol || prefixcmp(bol, sign_off_header)) + fprintf(fp, "\n"); + fprintf(fp, "%s%s\n", sign_off_header, git_committer_info(1)); + } + + 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); + + commitable = run_status(fp, index_file); + + 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 || 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 && 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(argv, prefix); + + commitable = run_status(stdout, index_file); + + rollback_lock_file(&lock_file); + + 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); +} + +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, parent_count = 0; + struct strbuf sb; + const char *index_file, *reflog_msg; + char *nl, *header_line; + 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(argv, prefix); + + if (!no_verify && run_hook(index_file, "pre-commit", NULL)) + exit(1); + + if (!prepare_log_message(index_file) && !in_merge) { + run_status(stdout, index_file); + unlink(commit_editmsg); + return 1; + } + + strbuf_init(&sb, 0); + + /* Start building up the commit header */ + read_cache_from(index_file); + active_cache_tree = cache_tree(); + if (cache_tree_update(active_cache_tree, + active_cache, active_nr, 0, 0) < 0) + die("Error building trees"); + strbuf_addf(&sb, "tree %s\n", + sha1_to_hex(active_cache_tree->sha1)); + + /* Determine parents */ + if (initial_commit) { + reflog_msg = "commit (initial)"; + parent_count = 0; + } 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) { + fprintf(stderr, "launching editor, log %s\n", logfile); + launch_editor(git_path(commit_editmsg), &sb); + } else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) + die("could not read commit message\n"); + if (run_hook(index_file, "commit-msg", commit_editmsg)) + exit(1); + stripspace(&sb, 1); + if (sb.len < header_len || + message_is_empty(&sb, header_len)) + 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)) + 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'); + header_line = xstrndup(sb.buf + header_len, + nl - (sb.buf + header_len)); + strbuf_release(&sb); + strbuf_addf(&sb, "%s: %s\n", reflog_msg, header_line); + strbuf_addch(&sb, '\0'); + free(header_line); + + if (!ref_lock) + die("cannot lock HEAD ref"); + if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) + die("cannot update HEAD ref"); + + unlink(git_path("MERGE_HEAD")); + unlink(git_path("MERGE_MSG")); + + if (lock_file.filename[0] && commit_locked_index(&lock_file)) + die("failed to write new index"); + + rerere(); + + run_hook(index_file, "post-commit", NULL); + + if (!quiet) + print_summary(prefix, commit_sha1); + + return 0; +} diff --git a/builtin.h b/builtin.h index bcb54aa..caea1a9 100644 --- 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); @@ -69,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); diff --git a/contrib/examples/git-commit.sh b/contrib/examples/git-commit.sh new file mode 100755 index 0000000..4853397 --- /dev/null +++ b/contrib/examples/git-commit.sh @@ -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 | -F | (-C|-c) | --amend] [-u] [-e] [--author ] [--template ] [[-i | -o] ...]' +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-commit.sh b/git-commit.sh deleted file mode 100755 index 4853397..0000000 --- a/git-commit.sh +++ /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 | -F | (-C|-c) | --amend] [-u] [-e] [--author ] [--template ] [[-i | -o] ...]' -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.c b/git.c index 80c2f14..a5adc34 100644 --- 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 }, @@ -342,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 }, -- cgit v0.10.2-6-g49f6 From e97c9ad96b60d51b0b3852aaaff146015d01cb62 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 8 Nov 2007 14:06:52 +0000 Subject: launch_editor(): read the file, even when EDITOR=: Earlier we just returned in case EDITOR=: but the message stored in the file was not read back. Fix this, at the same time simplifying the code as suggested by Johannes Sixt. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin-tag.c b/builtin-tag.c index 88a5449..566b9d1 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -20,8 +20,6 @@ static char signingkey[1000]; 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,17 +40,12 @@ void launch_editor(const char *path, struct strbuf *buffer) if (!editor) editor = "vi"; - if (!strcmp(editor, ":")) - return; - - 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", -- cgit v0.10.2-6-g49f6 From 741707b1e2261d522a92948ff7d7b897ff00e587 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 8 Nov 2007 12:15:26 +0000 Subject: builtin-commit: fix reflog message generation Instead of strdup()ing, we can just reuse the buffer in which the commit message is stored, and which is supposed to hold the reflog message anyway. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin-commit.c b/builtin-commit.c index 669cc6b..c8f79a8 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -488,7 +488,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) int header_len, parent_count = 0; struct strbuf sb; const char *index_file, *reflog_msg; - char *nl, *header_line; + char *nl; unsigned char commit_sha1[20]; struct ref_lock *ref_lock; @@ -585,12 +585,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix) 0); nl = strchr(sb.buf + header_len, '\n'); - header_line = xstrndup(sb.buf + header_len, - nl - (sb.buf + header_len)); - strbuf_release(&sb); - strbuf_addf(&sb, "%s: %s\n", reflog_msg, header_line); - strbuf_addch(&sb, '\0'); - free(header_line); + 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) die("cannot lock HEAD ref"); -- cgit v0.10.2-6-g49f6 From d37d320386369375b4e5b95b98517503125376f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Fri, 9 Nov 2007 11:40:27 -0500 Subject: builtin-commit: Refresh cache after adding files. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have promised our users that after running git-status or git-commit the index will be refreshed for a long time since these commands were introduced. Do refresh the index before writing it out to keep the promise. Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano diff --git a/builtin-commit.c b/builtin-commit.c index c8f79a8..a84a729 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -81,6 +81,7 @@ static char *prepare_index(const char **files, const char *prefix) if (all || also) { add_files_to_cache(verbose, also ? prefix : NULL, files); + refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close(fd)) die("unable to write new_index file"); return lock_file.filename; @@ -110,6 +111,7 @@ static char *prepare_index(const char **files, const char *prefix) fd = hold_lock_file_for_update(next_index_lock, git_path("next-index-%d", getpid()), 1); add_files_to_cache(verbose, prefix, files); + refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close(fd)) die("unable to write new_index file"); -- cgit v0.10.2-6-g49f6 From 367c98866c340bc9cf5cfa88c3b69f027165fc44 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 11 Nov 2007 17:35:41 +0000 Subject: git status: show relative paths when run in a subdirectory To show the relative paths, the function formerly called quote_crlf() (now called quote_path()) takes the prefix as an additional argument. While at it, the static buffers were replaced by strbufs. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin-commit.c b/builtin-commit.c index a84a729..400ee93 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -118,11 +118,12 @@ static char *prepare_index(const char **files, const char *prefix) return next_index_lock->filename; } -static int run_status(FILE *fp, const char *index_file) +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; @@ -140,7 +141,7 @@ static int run_status(FILE *fp, const char *index_file) static const char sign_off_header[] = "Signed-off-by: "; -static int prepare_log_message(const char *index_file) +static int prepare_log_message(const char *index_file, const char *prefix) { struct stat statbuf; int commitable; @@ -216,7 +217,7 @@ static int prepare_log_message(const char *index_file) if (only_include_assumed) fprintf(fp, "# %s\n", only_include_assumed); - commitable = run_status(fp, index_file); + commitable = run_status(fp, index_file, prefix); fclose(fp); @@ -409,7 +410,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) index_file = prepare_index(argv, prefix); - commitable = run_status(stdout, index_file); + commitable = run_status(stdout, index_file, prefix); rollback_lock_file(&lock_file); @@ -503,8 +504,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (!no_verify && run_hook(index_file, "pre-commit", NULL)) exit(1); - if (!prepare_log_message(index_file) && !in_merge) { - run_status(stdout, index_file); + if (!prepare_log_message(index_file, prefix) && !in_merge) { + run_status(stdout, index_file, prefix); unlink(commit_editmsg); return 1; } diff --git a/builtin-runstatus.c b/builtin-runstatus.c index 2db25c8..8d167a9 100644 --- a/builtin-runstatus.c +++ b/builtin-runstatus.c @@ -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")) diff --git a/t/t7502-status.sh b/t/t7502-status.sh new file mode 100755 index 0000000..269b334 --- /dev/null +++ b/t/t7502-status.sh @@ -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 ..." to unstage) +# +# new file: dir2/added +# +# Changed but not updated: +# (use "git add ..." to update what will be committed) +# +# modified: dir1/modified +# +# Untracked files: +# (use "git add ..." 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 ..." to unstage) +# +# new file: ../dir2/added +# +# Changed but not updated: +# (use "git add ..." to update what will be committed) +# +# modified: ../dir1/modified +# +# Untracked files: +# (use "git add ..." 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 diff --git a/wt-status.c b/wt-status.c index 9a6ef4a..d3c10b8 100644 --- a/wt-status.c +++ b/wt-status.c @@ -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,9 +305,11 @@ 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) diff --git a/wt-status.h b/wt-status.h index 7744932..f58ebcb 100644 --- a/wt-status.h +++ b/wt-status.h @@ -23,6 +23,7 @@ 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); -- cgit v0.10.2-6-g49f6 From 13208572fbe8838fd8835548d7502202d1f7b21d Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 11 Nov 2007 17:35:58 +0000 Subject: builtin-commit: fix --signoff The Signed-off-by: line contained a spurious timestamp. The reason was a call to git_committer_info(1), which automatically added the timestamp. Instead, fmt_ident() was taught to interpret an empty string for the date (as opposed to NULL, which still triggers the default behavior) as "do not bother with the timestamp", and builtin-commit.c uses it. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin-commit.c b/builtin-commit.c index 400ee93..780eec7 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -183,21 +183,29 @@ static int prepare_log_message(const char *index_file, const char *prefix) die("could not open %s\n", git_path(commit_editmsg)); stripspace(&sb, 0); - if (fwrite(sb.buf, 1, sb.len, fp) < sb.len) - die("could not write commit template: %s\n", - strerror(errno)); if (signoff) { - const char *info, *bol; - - info = git_committer_info(1); - strbuf_addch(&sb, '\0'); - bol = strrchr(sb.buf + sb.len - 1, '\n'); - if (!bol || prefixcmp(bol, sign_off_header)) - fprintf(fp, "\n"); - fprintf(fp, "%s%s\n", sign_off_header, git_committer_info(1)); + 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)) + 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) diff --git a/ident.c b/ident.c index 9b2a852..5be7533 100644 --- 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"); diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index cf389b8..49c1922 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -122,7 +122,19 @@ 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 +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 -- cgit v0.10.2-6-g49f6 From 2150554b0ed60356d8918b610834c04ad2eecdec Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 11 Nov 2007 17:36:27 +0000 Subject: builtin-commit --s: add a newline if the last line was not a S-o-b The rule is this: if the last line already contains the sign off by the current committer, do nothing. If it contains another sign off, just add the sign off of the current committer. If the last line does not contain a sign off, add a new line before adding the sign off. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin-commit.c b/builtin-commit.c index 780eec7..4dfa802 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -197,8 +197,11 @@ static int prepare_log_message(const char *index_file, const char *prefix) 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, sob.buf)) { + if (prefixcmp(sb.buf + i, sign_off_header)) + strbuf_addch(&sb, '\n'); strbuf_addbuf(&sb, &sob); + } strbuf_release(&sob); } diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index 49c1922..baed6ce 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -126,6 +126,7 @@ test_expect_success 'using alternate GIT_INDEX_FILE (2)' ' cat > expect << EOF zort + Signed-off-by: C O Mitter EOF -- cgit v0.10.2-6-g49f6 From f9568530c97757d840171c685fd623e1f68bc552 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 11 Nov 2007 17:36:39 +0000 Subject: builtin-commit: resurrect behavior for multiple -m options When more than one -m option is given, the message does not replace the previous, but is appended as a new paragraph. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin-commit.c b/builtin-commit.c index 4dfa802..ee79cf1 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -30,13 +30,27 @@ static char *use_message_buffer; static const char commit_editmsg[] = "COMMIT_EDITMSG"; static struct lock_file lock_file; -static char *logfile, *force_author, *message, *template_file; +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), @@ -45,7 +59,7 @@ static struct option builtin_commit_options[] = { OPT_STRING('F', "file", &logfile, "FILE", "read log from file"), OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"), - OPT_STRING('m', "message", &message, "MESSAGE", "specify commit message"), + 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"), @@ -150,8 +164,8 @@ static int prepare_log_message(const char *index_file, const char *prefix) FILE *fp; strbuf_init(&sb, 0); - if (message) { - strbuf_add(&sb, message, strlen(message)); + if (message.len) { + strbuf_addbuf(&sb, &message); } else if (logfile && !strcmp(logfile, "-")) { if (isatty(0)) fprintf(stderr, "(reading log message from standard input)\n"); @@ -322,7 +336,7 @@ static int parse_and_validate_options(int argc, const char *argv[]) argc = parse_options(argc, argv, builtin_commit_options, builtin_commit_usage, 0); - if (logfile || message || use_message) + if (logfile || message.len || use_message) no_edit = 1; if (edit_flag) no_edit = 0; @@ -347,7 +361,7 @@ static int parse_and_validate_options(int argc, const char *argv[]) f++; if (f > 1) die("Only one of -c/-C/-F can be used."); - if (message && f > 0) + if (message.len && f > 0) die("Option -m cannot be combined with -c/-C/-F."); if (edit_message) use_message = edit_message; -- cgit v0.10.2-6-g49f6 From 129fa606365c172d07a5d98bea9345277f221363 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 11 Nov 2007 17:36:52 +0000 Subject: builtin-commit: Add newline when showing which commit was created The function log_tree_commit() does not break the line, so we have to do it ourselves. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin-commit.c b/builtin-commit.c index ee79cf1..2233300 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -494,6 +494,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1) printf("Created %scommit ", initial_commit ? "initial " : ""); log_tree_commit(&rev, commit); + printf("\n"); } int git_commit_config(const char *k, const char *v) -- cgit v0.10.2-6-g49f6 From ef12b50d0cf0123377a6fb96584a287a6c24346b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 12 Nov 2007 15:48:22 -0500 Subject: Call refresh_cache() when updating the user index for --only commits. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're guaranteeing the user that the index will be stat-clean after git commit. Thus, we need to call refresh_cache() for the user index too, in the 'git commit ' case. Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano diff --git a/builtin-commit.c b/builtin-commit.c index 2233300..ee9fe72 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -109,6 +109,7 @@ static char *prepare_index(const char **files, const char *prefix) /* update the user index file */ add_files_to_cache(verbose, prefix, files); + refresh_cache(REFRESH_QUIET); if (write_cache(fd, active_cache, active_nr) || close(fd)) die("unable to write new_index file"); -- cgit v0.10.2-6-g49f6 From 18abc2dba4973c3c2cff286fac3340e95e0ee474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Wed, 14 Nov 2007 10:31:53 -0500 Subject: builtin-commit: Clean up an unused variable and a debug fprintf(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano diff --git a/builtin-commit.c b/builtin-commit.c index ee9fe72..5e2257c 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -515,7 +515,7 @@ static const char commit_utf8_warn[] = int cmd_commit(int argc, const char **argv, const char *prefix) { - int header_len, parent_count = 0; + int header_len; struct strbuf sb; const char *index_file, *reflog_msg; char *nl; @@ -551,7 +551,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix) /* Determine parents */ if (initial_commit) { reflog_msg = "commit (initial)"; - parent_count = 0; } else if (amend) { struct commit_list *c; struct commit *commit; @@ -592,10 +591,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix) /* Get the commit message and validate it */ header_len = sb.len; - if (!no_edit) { - fprintf(stderr, "launching editor, log %s\n", logfile); + if (!no_edit) launch_editor(git_path(commit_editmsg), &sb); - } else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) + else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) die("could not read commit message\n"); if (run_hook(index_file, "commit-msg", commit_editmsg)) exit(1); -- cgit v0.10.2-6-g49f6 From 1200993a1e885fd67d1a1d63da9d2a0e1ee5bcea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Thu, 15 Nov 2007 09:49:58 -0500 Subject: t7501-commit: Add test for git commit with dirty index. Signed-off-by: Junio C Hamano diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index e601028..ce83af3 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -257,4 +257,14 @@ test_expect_success 'amend commit to fix author' ' diff expected current ' + +test_expect_success 'git commit 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 -- cgit v0.10.2-6-g49f6 From 637efc3456576d548ed5b42e70deffca42e7428e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 15 Nov 2007 06:27:57 +0000 Subject: Replace "runstatus" with "status" in the tests We no longer have "runstatus", but running "status" is no longer that expensive anyway; it is a builtin. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index ae0639d..e25b255 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh @@ -86,7 +86,7 @@ EOF git config core.excludesFile excludes-file -git runstatus | grep "^# " > output +git status | grep "^# " > output cat > expect << EOF # .gitignore diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh index 063e792..877c1ea 100755 --- a/t/t4001-diff-rename.sh +++ b/t/t4001-diff-rename.sh @@ -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 -- cgit v0.10.2-6-g49f6 From a50f9fc5feb0a8b8afe51e75ae7c7a87446113e3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 18 Nov 2007 01:58:16 -0800 Subject: file_exists(): dangling symlinks do exist This function is used to see if a path given by the user does exist on the filesystem. A symbolic link that does not point anywhere does exist but running stat() on it would yield an error, and it incorrectly said it does not exist. Signed-off-by: Junio C Hamano diff --git a/dir.c b/dir.c index 225fdfb..11a4cf3 100644 --- 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; } /* -- cgit v0.10.2-6-g49f6 From bc5d248a9fba11cb78dd0a3a91938d881dec1245 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 18 Nov 2007 12:01:38 -0800 Subject: builtin-commit: do not color status output shown in the message template Noticed by Ping Yin on the list. Signed-off-by: Junio C Hamano diff --git a/builtin-commit.c b/builtin-commit.c index 5e2257c..7616dd1 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -159,7 +159,7 @@ 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; + int commitable, saved_color_setting; struct strbuf sb; char *buffer; FILE *fp; @@ -243,7 +243,10 @@ static int prepare_log_message(const char *index_file, const char *prefix) 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); diff --git a/wt-status.h b/wt-status.h index f58ebcb..225fb4d 100644 --- a/wt-status.h +++ b/wt-status.h @@ -27,6 +27,7 @@ struct wt_status { }; 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); -- cgit v0.10.2-6-g49f6 From e06ad5ffb9f8d4207d676fadd675cf1b949ce358 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 18 Nov 2007 12:21:17 -0800 Subject: builtin-commit: run commit-msg hook with correct message file It should run with $GIT_DIR/COMMIT_EDITMSG, not just COMMIT_EDITMSG. Signed-off-by: Junio C Hamano diff --git a/builtin-commit.c b/builtin-commit.c index 7616dd1..cd2f5ca 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -598,7 +598,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) launch_editor(git_path(commit_editmsg), &sb); else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) die("could not read commit message\n"); - if (run_hook(index_file, "commit-msg", commit_editmsg)) + if (run_hook(index_file, "commit-msg", git_path(commit_editmsg))) exit(1); stripspace(&sb, 1); if (sb.len < header_len || -- cgit v0.10.2-6-g49f6 From ee425e4643aa2d7be72cb4586d7554cecce44d6e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 18 Nov 2007 01:13:32 -0800 Subject: Export three helper functions from ls-files This exports three helper functions from ls-files. * pathspec_match() checks if a given path matches a set of pathspecs and optionally records which pathspec was used. This function used to be called "match()" but renamed to be a bit less vague. * report_path_error() takes a set of pathspecs and the record pathspec_match() above leaves, and gives error message. This was split out of the main function of ls-files. * overlay_tree_on_cache() takes a tree-ish (typically "HEAD") and overlays it on the current in-core index. By iterating over the resulting index, the caller can find out the paths in either the index or the HEAD. This function used to be called "overlay_tree()" but renamed to be a bit more descriptive. Signed-off-by: Junio C Hamano diff --git a/builtin-ls-files.c b/builtin-ls-files.c index 7f60709..0f0ab2d 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -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=] [--exclude-from=] " @@ -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/cache.h b/cache.h index 33ebccf..26eec22 100644 --- a/cache.h +++ b/cache.h @@ -610,4 +610,10 @@ 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 */ -- cgit v0.10.2-6-g49f6 From b6ec1d619fb54642388063a88e2255556cf5de06 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 18 Nov 2007 01:12:04 -0800 Subject: Fix add_files_to_cache() to take pathspec, not user specified list of files This separates the logic to limit the extent of change to the index by where you are (controlled by "prefix") and what you specify from the command line (controlled by "pathspec"). Signed-off-by: Junio C Hamano diff --git a/builtin-add.c b/builtin-add.c index cf815a0..03508d3 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -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; @@ -180,9 +180,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/cache.h b/cache.h index 26eec22..cf0bdc6 100644 --- a/cache.h +++ b/cache.h @@ -604,13 +604,15 @@ 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); diff --git a/commit.h b/commit.h index aa67986..f450aae 100644 --- 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(void); -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) -- cgit v0.10.2-6-g49f6 From 2888605c649ccd423232161186d72c0e6c458a48 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 18 Nov 2007 01:52:55 -0800 Subject: builtin-commit: fix partial-commit support When making a partial-commit, we need to prepare two index files, one to be used to write out the tree to be committed (temporary index) and the other to be used as the index file after the commit is made. The temporary index needs to be initialized to HEAD and then all the named paths on the command line need to be staged on top of the index. For this, running add_files_to_cache() that compares what is in the index and the paths given from the command line is not enough -- the comparison will miss the paths that the user previously ran "git add" to the index since the HEAD because the index reset to the HEAD would not know about them. The index file needs to get the same modification done when preparing the temporary index as described above. This implementation mimics the behaviour of the scripted version of git-commit. It first runs overlay_tree_on_cache(), which was stolen from ls-files with the earlier change, to get the list of paths that the user can potentially mean, and then uses pathspec_match() to find which ones the user meant. This list of paths is used to update both the temporary and the real index file. Additional fixes are: - read the index file after pre-commit hook returns, as the hook can modify it to affect the contents of the commit. - remove the temporary index file .git/next-index-* after commit is done or aborted. - run post-commit hook with the real index file to be used after the commit (previously it gave the temporary commit if a partial commit was made). - resurrect the safety mechanism to refuse partial commits during a merge to match the scripted version. Signed-off-by: Junio C Hamano diff --git a/builtin-commit.c b/builtin-commit.c index cd2f5ca..e779db8 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -7,6 +7,7 @@ #include "cache.h" #include "cache-tree.h" +#include "dir.h" #include "builtin.h" #include "diff.h" #include "diffcore.h" @@ -19,6 +20,7 @@ #include "strbuf.h" #include "utf8.h" #include "parse-options.h" +#include "path-list.h" static const char * const builtin_commit_usage[] = { "git-commit [options] [--] ...", @@ -28,7 +30,13 @@ static const char * const builtin_commit_usage[] = { 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 lock_file; +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; @@ -78,41 +86,179 @@ static struct option builtin_commit_options[] = { 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(const char **files, const char *prefix) { int fd; struct tree *tree; - struct lock_file *next_index_lock; + struct path_list partial; + const char **pathspec = NULL; if (interactive) { interactive_add(); + commit_style = COMMIT_AS_IS; return get_index_file(); } - fd = hold_locked_index(&lock_file, 1); if (read_cache() < 0) die("index file corrupt"); - if (all || also) { - add_files_to_cache(verbose, also ? prefix : NULL, files); + if (*files) + pathspec = get_pathspec(prefix, files); + + /* + * 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"); - return lock_file.filename; + commit_style = COMMIT_NORMAL; + return index_lock.filename; } - if (*files == NULL) { - /* Commit index as-is. */ - rollback_lock_file(&lock_file); + /* + * 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(); } - /* update the user index file */ - add_files_to_cache(verbose, prefix, files); + /* + * 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) @@ -120,17 +266,12 @@ static char *prepare_index(const char **files, const char *prefix) if (read_tree(tree, 0, NULL)) die("failed to read HEAD tree object"); } - - /* Use a lock file to garbage collect the temporary index file. */ - next_index_lock = xmalloc(sizeof(*next_index_lock)); - fd = hold_lock_file_for_update(next_index_lock, - git_path("next-index-%d", getpid()), 1); - add_files_to_cache(verbose, prefix, files); + 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"); - return next_index_lock->filename; + 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) @@ -441,7 +582,7 @@ int cmd_status(int argc, const char **argv, const char *prefix) commitable = run_status(stdout, index_file, prefix); - rollback_lock_file(&lock_file); + rollback_index_files(); return commitable ? 0 : 1; } @@ -531,23 +672,36 @@ int cmd_commit(int argc, const char **argv, const char *prefix) index_file = prepare_index(argv, prefix); - if (!no_verify && run_hook(index_file, "pre-commit", NULL)) - exit(1); + 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; } - strbuf_init(&sb, 0); - - /* Start building up the commit header */ + /* + * 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); - active_cache_tree = cache_tree(); + if (!active_cache_tree) + active_cache_tree = cache_tree(); if (cache_tree_update(active_cache_tree, - active_cache, active_nr, 0, 0) < 0) + 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)); @@ -596,20 +750,27 @@ int cmd_commit(int argc, const char **argv, const char *prefix) 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) + 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))) + } + if (run_hook(index_file, "commit-msg", git_path(commit_editmsg))) { + rollback_index_files(); exit(1); + } stripspace(&sb, 1); - if (sb.len < header_len || - message_is_empty(&sb, header_len)) + 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)) + 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, @@ -624,21 +785,22 @@ int cmd_commit(int argc, const char **argv, const char *prefix) strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg)); strbuf_insert(&sb, strlen(reflog_msg), ": ", 2); - if (!ref_lock) + if (!ref_lock) { + rollback_index_files(); die("cannot lock HEAD ref"); - if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) + } + 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")); - if (lock_file.filename[0] && commit_locked_index(&lock_file)) - die("failed to write new index"); + commit_index_files(); rerere(); - - run_hook(index_file, "post-commit", NULL); - + run_hook(get_index_file(), "post-commit", NULL); if (!quiet) print_summary(prefix, commit_sha1); -- cgit v0.10.2-6-g49f6 From 99a12694582e2148fcd492f1eedaddcfe2a21621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Wed, 21 Nov 2007 21:54:49 -0500 Subject: builtin-commit: Include the diff in the commit message when verbose. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit run_diff_index() and the entire diff machinery is hard coded to output to stdout, so just redirect that and restore it when done. Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano diff --git a/builtin-commit.c b/builtin-commit.c index e779db8..4de316a 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -662,7 +662,7 @@ 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; + char *nl, *p; unsigned char commit_sha1[20]; struct ref_lock *ref_lock; @@ -758,6 +758,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix) 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(); diff --git a/wt-status.c b/wt-status.c index d3c10b8..0e0439f 100644 --- a/wt-status.c +++ b/wt-status.c @@ -315,12 +315,28 @@ static void wt_status_print_untracked(struct wt_status *s) 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) -- cgit v0.10.2-6-g49f6 From 12db334e75ae291aa69987cbe0feda2b6a64af38 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 22 Nov 2007 01:47:13 -0800 Subject: git-add -i: allow multiple selection in patch subcommand This allows more than one files from the list to be chosen from the patch subcommand instead of going through the file one by one. This also updates the "list-and-choose" UI for usability. When the prompt ends with ">>", if you type '*' to choose all choices, the prompt immediately returns the choice without requiring an extra empty line to confirm the selection. Signed-off-by: Junio C Hamano diff --git a/git-add--interactive.perl b/git-add--interactive.perl index a0e480e..e347216 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -260,7 +260,7 @@ sub list_and_choose { $chosen[$i] = $choose; } } - last if ($opts->{IMMEDIATE}); + last if ($opts->{IMMEDIATE} || $line eq '*'); } for ($i = 0; $i < @stuff; $i++) { if ($chosen[$i]) { @@ -567,12 +567,12 @@ sub patch_update_cmd { @mods = grep { !($_->{BINARY}) } @mods; return if (!@mods); - my ($it) = list_and_choose({ PROMPT => 'Patch update', - SINGLETON => 1, - IMMEDIATE => 1, - HEADER => $status_head, }, - @mods); - patch_update_file($it->{VALUE}) if ($it); + my (@them) = list_and_choose({ PROMPT => 'Patch update', + HEADER => $status_head, }, + @mods); + for (@them) { + patch_update_file($_->{VALUE}); + } } sub patch_update_file { -- cgit v0.10.2-6-g49f6 From b468f0ce4881bf42ffc820b1cddad67dad17fd80 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 22 Nov 2007 16:21:49 -0800 Subject: Add a few more tests for git-commit Signed-off-by: Junio C Hamano diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh new file mode 100755 index 0000000..21ac785 --- /dev/null +++ b/t/t7502-commit.sh @@ -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 -- cgit v0.10.2-6-g49f6 From 81bf96bb2ecb8c869cc365a7fee2c4d2e937a43c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 23 Nov 2007 02:37:03 -0800 Subject: builtin-apply: rename "whitespace" variables and fix styles The variables were somewhat misnamed. * "What to do when whitespace errors are detected" is now called "ws_error_action" (used to be called "new_whitespace"); * The constants to denote the possible actions are "nowarn_ws_error", "warn_on_ws_error", "die_on_ws_error", and "correct_ws_error". The last one used to be "strip_whitespace", but we correct whitespace error in indent (SP followed by HT) and "strip" is not quite an accurate name for it. Other than the renaming of variables and constants, there is no functional change in this patch. While we are at it, it also fixes overly long lines and multi-line comment styles (which of course do not affect the generated code at all). Signed-off-by: Junio C Hamano diff --git a/builtin-apply.c b/builtin-apply.c index 8411b38..eb09bfe 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -47,12 +47,12 @@ static unsigned long p_context = ULONG_MAX; static const char apply_usage[] = "git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=] ..."; -static enum whitespace_eol { - nowarn_whitespace, - warn_on_whitespace, - error_on_whitespace, - strip_whitespace, -} new_whitespace = warn_on_whitespace; +static enum ws_error_action { + nowarn_ws_error, + warn_on_ws_error, + die_on_ws_error, + correct_ws_error, +} ws_error_action = warn_on_ws_error; static int whitespace_error; static int squelch_whitespace_errors = 5; static int applied_after_fixing_ws; @@ -61,28 +61,28 @@ static const char *patch_input_file; static void parse_whitespace_option(const char *option) { if (!option) { - new_whitespace = warn_on_whitespace; + ws_error_action = warn_on_ws_error; return; } if (!strcmp(option, "warn")) { - new_whitespace = warn_on_whitespace; + ws_error_action = warn_on_ws_error; return; } if (!strcmp(option, "nowarn")) { - new_whitespace = nowarn_whitespace; + ws_error_action = nowarn_ws_error; return; } if (!strcmp(option, "error")) { - new_whitespace = error_on_whitespace; + ws_error_action = die_on_ws_error; return; } if (!strcmp(option, "error-all")) { - new_whitespace = error_on_whitespace; + ws_error_action = die_on_ws_error; squelch_whitespace_errors = 0; return; } - if (!strcmp(option, "strip")) { - new_whitespace = strip_whitespace; + if (!strcmp(option, "strip") || !strcmp(option, "fix")) { + ws_error_action = correct_ws_error; return; } die("unrecognized whitespace option '%s'", option); @@ -90,11 +90,8 @@ static void parse_whitespace_option(const char *option) static void set_default_whitespace_mode(const char *whitespace_option) { - if (!whitespace_option && !apply_default_whitespace) { - new_whitespace = (apply - ? warn_on_whitespace - : nowarn_whitespace); - } + if (!whitespace_option && !apply_default_whitespace) + ws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error); } /* @@ -137,6 +134,11 @@ struct fragment { #define BINARY_DELTA_DEFLATED 1 #define BINARY_LITERAL_DEFLATED 2 +/* + * This represents a "patch" to a file, both metainfo changes + * such as creation/deletion, filemode and content changes represented + * as a series of fragments. + */ struct patch { char *new_name, *old_name, *def_name; unsigned int old_mode, new_mode; @@ -158,7 +160,8 @@ struct patch { struct patch *next; }; -static void say_patch_name(FILE *output, const char *pre, struct patch *patch, const char *post) +static void say_patch_name(FILE *output, const char *pre, + struct patch *patch, const char *post) { fputs(pre, output); if (patch->old_name && patch->new_name && @@ -229,7 +232,8 @@ static char *find_name(const char *line, char *def, int p_value, int terminate) if (*line == '"') { struct strbuf name; - /* Proposed "new-style" GNU patch/diff format; see + /* + * Proposed "new-style" GNU patch/diff format; see * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2 */ strbuf_init(&name, 0); @@ -499,7 +503,8 @@ static int gitdiff_dissimilarity(const char *line, struct patch *patch) static int gitdiff_index(const char *line, struct patch *patch) { - /* index line is N hexadecimal, "..", N hexadecimal, + /* + * index line is N hexadecimal, "..", N hexadecimal, * and optional space with octal mode. */ const char *ptr, *eol; @@ -550,7 +555,8 @@ static const char *stop_at_slash(const char *line, int llen) return NULL; } -/* This is to extract the same name that appears on "diff --git" +/* + * This is to extract the same name that appears on "diff --git" * line. We do not find and return anything if it is a rename * patch, and it is OK because we will find the name elsewhere. * We need to reliably find name only when it is mode-change only, @@ -584,7 +590,8 @@ static char *git_header_name(char *line, int llen) goto free_and_fail1; strbuf_remove(&first, 0, cp + 1 - first.buf); - /* second points at one past closing dq of name. + /* + * second points at one past closing dq of name. * find the second name. */ while ((second < line + llen) && isspace(*second)) @@ -627,7 +634,8 @@ static char *git_header_name(char *line, int llen) return NULL; name++; - /* since the first name is unquoted, a dq if exists must be + /* + * since the first name is unquoted, a dq if exists must be * the beginning of the second name. */ for (second = name; second < line + llen; second++) { @@ -759,7 +767,7 @@ static int parse_num(const char *line, unsigned long *p) } static int parse_range(const char *line, int len, int offset, const char *expect, - unsigned long *p1, unsigned long *p2) + unsigned long *p1, unsigned long *p2) { int digits, ex; @@ -868,14 +876,14 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc return offset; } - /** --- followed by +++ ? */ + /* --- followed by +++ ? */ if (memcmp("--- ", line, 4) || memcmp("+++ ", line + len, 4)) continue; /* * We only accept unified patches, so we want it to * at least have "@@ -a,b +c,d @@\n", which is 14 chars - * minimum + * minimum ("@@ -0,0 +1 @@\n" is the shortest). */ nextlen = linelen(line + len, size - len); if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4)) @@ -932,14 +940,14 @@ static void check_whitespace(const char *line, int len) err, patch_input_file, linenr, len-2, line+1); } - /* * Parse a unified diff. Note that this really needs to parse each * fragment separately, since the only way to know the difference * between a "---" that is part of a patch, and a "---" that starts * the next patch is to look at the line counts.. */ -static int parse_fragment(char *line, unsigned long size, struct patch *patch, struct fragment *fragment) +static int parse_fragment(char *line, unsigned long size, + struct patch *patch, struct fragment *fragment) { int added, deleted; int len = linelen(line, size), offset; @@ -980,7 +988,7 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s break; case '-': if (apply_in_reverse && - new_whitespace != nowarn_whitespace) + ws_error_action != nowarn_ws_error) check_whitespace(line, len); deleted++; oldlines--; @@ -988,14 +996,15 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s break; case '+': if (!apply_in_reverse && - new_whitespace != nowarn_whitespace) + ws_error_action != nowarn_ws_error) check_whitespace(line, len); added++; newlines--; trailing = 0; break; - /* We allow "\ No newline at end of file". Depending + /* + * We allow "\ No newline at end of file". Depending * on locale settings when the patch was produced we * don't know what this line looks like. The only * thing we do know is that it begins with "\ ". @@ -1013,7 +1022,8 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s fragment->leading = leading; fragment->trailing = trailing; - /* If a fragment ends with an incomplete line, we failed to include + /* + * If a fragment ends with an incomplete line, we failed to include * it in the above loop because we hit oldlines == newlines == 0 * before seeing it. */ @@ -1141,7 +1151,8 @@ static struct fragment *parse_binary_hunk(char **buf_p, int *status_p, int *used_p) { - /* Expect a line that begins with binary patch method ("literal" + /* + * Expect a line that begins with binary patch method ("literal" * or "delta"), followed by the length of data before deflating. * a sequence of 'length-byte' followed by base-85 encoded data * should follow, terminated by a newline. @@ -1190,7 +1201,8 @@ static struct fragment *parse_binary_hunk(char **buf_p, size--; break; } - /* Minimum line is "A00000\n" which is 7-byte long, + /* + * Minimum line is "A00000\n" which is 7-byte long, * and the line length must be multiple of 5 plus 2. */ if ((llen < 7) || (llen-2) % 5) @@ -1241,7 +1253,8 @@ static struct fragment *parse_binary_hunk(char **buf_p, static int parse_binary(char *buffer, unsigned long size, struct patch *patch) { - /* We have read "GIT binary patch\n"; what follows is a line + /* + * We have read "GIT binary patch\n"; what follows is a line * that says the patch method (currently, either "literal" or * "delta") and the length of data before deflating; a * sequence of 'length-byte' followed by base-85 encoded data @@ -1271,7 +1284,8 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch) if (reverse) used += used_1; else if (status) { - /* not having reverse hunk is not an error, but having + /* + * Not having reverse hunk is not an error, but having * a corrupt reverse hunk is. */ free((void*) forward->patch); @@ -1292,7 +1306,8 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) if (offset < 0) return offset; - patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch); + patchsize = parse_single_patch(buffer + offset + hdrsize, + size - offset - hdrsize, patch); if (!patchsize) { static const char *binhdr[] = { @@ -1368,8 +1383,10 @@ static void reverse_patches(struct patch *p) } } -static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; -static const char minuses[]= "----------------------------------------------------------------------"; +static const char pluses[] = +"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; +static const char minuses[]= +"----------------------------------------------------------------------"; static void show_stats(struct patch *patch) { @@ -1438,7 +1455,9 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf) } } -static int find_offset(const char *buf, unsigned long size, const char *fragment, unsigned long fragsize, int line, int *lines) +static int find_offset(const char *buf, unsigned long size, + const char *fragment, unsigned long fragsize, + int line, int *lines) { int i; unsigned long start, backwards, forwards; @@ -1539,7 +1558,8 @@ static void remove_last_line(const char **rbuf, int *rsize) static int apply_line(char *output, const char *patch, int plen) { - /* plen is number of bytes to be copied from patch, + /* + * plen is number of bytes to be copied from patch, * starting at patch+1 (patch[0] is '+'). Typically * patch[plen] is '\n', unless this is the incomplete * last line. @@ -1552,12 +1572,15 @@ static int apply_line(char *output, const char *patch, int plen) int need_fix_leading_space = 0; char *buf; - if ((new_whitespace != strip_whitespace) || !whitespace_error || + if ((ws_error_action != correct_ws_error) || !whitespace_error || *patch != '+') { memcpy(output, patch + 1, plen); return plen; } + /* + * Strip trailing whitespace + */ if (1 < plen && isspace(patch[plen-1])) { if (patch[plen] == '\n') add_nl_to_tail = 1; @@ -1567,6 +1590,9 @@ static int apply_line(char *output, const char *patch, int plen) fixed = 1; } + /* + * Check leading whitespaces (indent) + */ for (i = 1; i < plen; i++) { char ch = patch[i]; if (ch == '\t') { @@ -1583,7 +1609,8 @@ static int apply_line(char *output, const char *patch, int plen) buf = output; if (need_fix_leading_space) { int consecutive_spaces = 0; - /* between patch[1..last_tab_in_indent] strip the + /* + * between patch[1..last_tab_in_indent] strip the * funny spaces, updating them to tab as needed. */ for (i = 1; i < last_tab_in_indent; i++, plen--) { @@ -1613,7 +1640,8 @@ static int apply_line(char *output, const char *patch, int plen) return output + plen - buf; } -static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int inaccurate_eof) +static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, + int inaccurate_eof) { int match_beginning, match_end; const char *patch = frag->patch; @@ -1695,8 +1723,9 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina size -= len; } - if (inaccurate_eof && oldsize > 0 && old[oldsize - 1] == '\n' && - newsize > 0 && new[newsize - 1] == '\n') { + if (inaccurate_eof && + oldsize > 0 && old[oldsize - 1] == '\n' && + newsize > 0 && new[newsize - 1] == '\n') { oldsize--; newsize--; } @@ -1733,7 +1762,7 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina if (match_beginning && offset) offset = -1; if (offset >= 0) { - if (new_whitespace == strip_whitespace && + if (ws_error_action == correct_ws_error && (buf->len - oldsize - offset == 0)) /* end of file? */ newsize -= new_blank_lines_at_end; @@ -1758,9 +1787,10 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina match_beginning = match_end = 0; continue; } - /* Reduce the number of context lines - * Reduce both leading and trailing if they are equal - * otherwise just reduce the larger context. + /* + * Reduce the number of context lines; reduce both + * leading and trailing if they are equal otherwise + * just reduce the larger context. */ if (leading >= trailing) { remove_first_line(&oldlines, &oldsize); @@ -1820,7 +1850,8 @@ static int apply_binary(struct strbuf *buf, struct patch *patch) const char *name = patch->old_name ? patch->old_name : patch->new_name; unsigned char sha1[20]; - /* For safety, we require patch index line to contain + /* + * For safety, we require patch index line to contain * full 40-byte textual SHA1 for old and new, at least for now. */ if (strlen(patch->old_sha1_prefix) != 40 || @@ -1831,7 +1862,8 @@ static int apply_binary(struct strbuf *buf, struct patch *patch) "without full index line", name); if (patch->old_name) { - /* See if the old one matches what the patch + /* + * See if the old one matches what the patch * applies to. */ hash_sha1_file(buf->buf, buf->len, blob_type, sha1); @@ -1868,7 +1900,8 @@ static int apply_binary(struct strbuf *buf, struct patch *patch) /* XXX read_sha1_file NUL-terminates */ strbuf_attach(buf, result, size, size + 1); } else { - /* We have verified buf matches the preimage; + /* + * We have verified buf matches the preimage; * apply the patch data to it, which is stored * in the patch->fragments->{patch,size}. */ @@ -2067,7 +2100,8 @@ static int check_patch(struct patch *patch, struct patch *prev_patch) if (new_name && prev_patch && 0 < prev_patch->is_delete && !strcmp(prev_patch->old_name, new_name)) - /* A type-change diff is always split into a patch to + /* + * A type-change diff is always split into a patch to * delete old, immediately followed by a patch to * create new (see diff.c::run_diff()); in such a case * it is Ok that the entry to be deleted by the @@ -2671,7 +2705,7 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof) offset += nr; } - if (whitespace_error && (new_whitespace == error_on_whitespace)) + if (whitespace_error && (ws_error_action == die_on_ws_error)) apply = 0; update_index = check_index && apply; @@ -2866,7 +2900,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix) squelched, squelched == 1 ? "" : "s"); } - if (new_whitespace == error_on_whitespace) + if (ws_error_action == die_on_ws_error) die("%d line%s add%s whitespace errors.", whitespace_error, whitespace_error == 1 ? "" : "s", -- cgit v0.10.2-6-g49f6 From d5a4164140c52a2d267d90e2413d1fe4a326a386 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 23 Nov 2007 20:14:20 -0800 Subject: builtin-apply: teach whitespace_rules We earlier introduced core.whitespace to allow users to tweak the definition of what the "whitespace errors" are, for the purpose of diff output highlighting. This teaches the same to git-apply, so that the command can both detect (when --whitespace=warn option is given) and fix (when --whitespace=fix option is given) as configured. Signed-off-by: Junio C Hamano diff --git a/builtin-apply.c b/builtin-apply.c index eb09bfe..e04b493 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -910,23 +910,35 @@ static void check_whitespace(const char *line, int len) * this function. That is, an addition of an empty line would * check the '+' here. Sneaky... */ - if (isspace(line[len-2])) + if ((whitespace_rule & WS_TRAILING_SPACE) && isspace(line[len-2])) goto error; /* * Make sure that there is no space followed by a tab in * indentation. */ - err = "Space in indent is followed by a tab"; - for (i = 1; i < len; i++) { - if (line[i] == '\t') { - if (seen_space) - goto error; - } - else if (line[i] == ' ') - seen_space = 1; - else - break; + if (whitespace_rule & WS_SPACE_BEFORE_TAB) { + err = "Space in indent is followed by a tab"; + for (i = 1; i < len; i++) { + if (line[i] == '\t') { + if (seen_space) + goto error; + } + else if (line[i] == ' ') + seen_space = 1; + else + break; + } + } + + /* + * Make sure that the indentation does not contain more than + * 8 spaces. + */ + if ((whitespace_rule & WS_INDENT_WITH_NON_TAB) && + (8 < len) && !strncmp("+ ", line, 9)) { + err = "Indent more than 8 places with spaces"; + goto error; } return; @@ -1581,7 +1593,8 @@ static int apply_line(char *output, const char *patch, int plen) /* * Strip trailing whitespace */ - if (1 < plen && isspace(patch[plen-1])) { + if ((whitespace_rule & WS_TRAILING_SPACE) && + (1 < plen && isspace(patch[plen-1]))) { if (patch[plen] == '\n') add_nl_to_tail = 1; plen--; @@ -1597,11 +1610,16 @@ static int apply_line(char *output, const char *patch, int plen) char ch = patch[i]; if (ch == '\t') { last_tab_in_indent = i; - if (0 <= last_space_in_indent) + if ((whitespace_rule & WS_SPACE_BEFORE_TAB) && + 0 <= last_space_in_indent) + need_fix_leading_space = 1; + } else if (ch == ' ') { + last_space_in_indent = i; + if ((whitespace_rule & WS_INDENT_WITH_NON_TAB) && + last_tab_in_indent < 0 && + 8 <= i) need_fix_leading_space = 1; } - else if (ch == ' ') - last_space_in_indent = i; else break; } @@ -1609,11 +1627,21 @@ static int apply_line(char *output, const char *patch, int plen) buf = output; if (need_fix_leading_space) { int consecutive_spaces = 0; + int last = last_tab_in_indent + 1; + + if (whitespace_rule & WS_INDENT_WITH_NON_TAB) { + /* have "last" point at one past the indent */ + if (last_tab_in_indent < last_space_in_indent) + last = last_space_in_indent + 1; + else + last = last_tab_in_indent + 1; + } + /* - * between patch[1..last_tab_in_indent] strip the - * funny spaces, updating them to tab as needed. + * between patch[1..last], strip the funny spaces, + * updating them to tab as needed. */ - for (i = 1; i < last_tab_in_indent; i++, plen--) { + for (i = 1; i < last; i++, plen--) { char ch = patch[i]; if (ch != ' ') { consecutive_spaces = 0; @@ -1626,8 +1654,10 @@ static int apply_line(char *output, const char *patch, int plen) } } } + while (0 < consecutive_spaces--) + *output++ = ' '; fixed = 1; - i = last_tab_in_indent; + i = last; } else i = 1; diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh new file mode 100755 index 0000000..f53ac46 --- /dev/null +++ b/t/t4124-apply-ws-rule.sh @@ -0,0 +1,133 @@ +#!/bin/sh + +test_description='core.whitespace rules and git-apply' + +. ./test-lib.sh + +prepare_test_file () { + + # A line that has character X is touched iff RULE is in effect: + # X RULE + # ! trailing-space + # @ space-before-tab + # # indent-with-non-tab + sed -e "s/_/ /g" -e "s/>/ /" <<-\EOF + An_SP in an ordinary line>and a HT. + >A HT. + _>A SP and a HT (@). + _>_A SP, a HT and a SP (@). + _______Seven SP. + ________Eight SP (#). + _______>Seven SP and a HT (@). + ________>Eight SP and a HT (@#). + _______>_Seven SP, a HT and a SP (@). + ________>_Eight SP, a HT and a SP (@#). + _______________Fifteen SP (#). + _______________>Fifteen SP and a HT (@#). + ________________Sixteen SP (#). + ________________>Sixteen SP and a HT (@#). + _____a__Five SP, a non WS, two SP. + A line with a (!) trailing SP_ + A line with a (!) trailing HT> + EOF +} + +apply_patch () { + >target && + sed -e "s|\([ab]\)/file|\1/target|" //p" >fixed + + # the changed lines are all expeced to change + fixed_cnt=$(wc -l fixed-patch + test -s fixed-patch && return 0 + + # Make sure it is complaint-free + >target + git apply --whitespace=error-all file && + git add file && + prepare_test_file >file && + git diff-files -p >patch && + >target && + git add target + +' + +test_expect_success 'whitespace=nowarn, default rule' ' + + apply_patch --whitespace=nowarn && + diff file target + +' + +test_expect_success 'whitespace=warn, default rule' ' + + apply_patch --whitespace=warn && + diff file target + +' + +test_expect_success 'whitespace=error-all, default rule' ' + + apply_patch --whitespace=error-all && return 1 + test -s target && return 1 + : happy + +' + +test_expect_success 'whitespace=error-all, no rule' ' + + git config core.whitespace -trailing,-space-before,-indent && + apply_patch --whitespace=error-all && + diff file target + +' + +for t in - '' +do + case "$t" in '') tt='!' ;; *) tt= ;; esac + for s in - '' + do + case "$s" in '') ts='@' ;; *) ts= ;; esac + for i in - '' + do + case "$i" in '') ti='#' ;; *) ti= ;; esac + rule=${t}trailing,${s}space,${i}indent && + test_expect_success "rule=$rule" ' + git config core.whitespace "$rule" && + test_fix "$tt$ts$ti" + ' + done + done +done + +test_done -- cgit v0.10.2-6-g49f6 From 91af7ae54f2a0af453c3a5ac612ed613b38b4fdf Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 24 Nov 2007 11:57:41 -0800 Subject: core.whitespace: documentation updates. This adds description of core.whitespace to the manual page of git-config, and updates the stale description of whitespace handling in the manual page of git-apply. Also demote "strip" to a synonym status for "fix" as the value of --whitespace option given to git-apply. Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index edf50cd..0e71137 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -293,6 +293,20 @@ core.pager:: The command that git will use to paginate output. Can be overridden with the `GIT_PAGER` environment variable. +core.whitespace:: + A comma separated list of common whitespace problems to + notice. `git diff` will use `color.diff.whitespace` to + highlight them, and `git apply --whitespace=error` will + consider them as errors: ++ +* `trailing-space` treats trailing whitespaces at the end of the line + as an error (enabled by default). +* `space-before-tab` treats a space character that appears immediately + before a tab character in the initial indent part of the line as an + error (enabled by default). +* `indent-with-non-tab` treats a line that is indented with 8 or more + space characters that can be replaced with tab characters. + alias.*:: Command aliases for the gitlink:git[1] command wrapper - e.g. after defining "alias.last = cat-file commit HEAD", the invocation @@ -378,8 +392,8 @@ color.diff.:: which part of the patch to use the specified color, and is one of `plain` (context text), `meta` (metainformation), `frag` (hunk header), `old` (removed lines), `new` (added lines), - `commit` (commit headers), or `whitespace` (highlighting dubious - whitespace). The values of these variables may be specified as + `commit` (commit headers), or `whitespace` (highlighting + whitespace errors). The values of these variables may be specified as in color.branch.. color.pager:: diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt index c1c54bf..bae3e7b 100644 --- a/Documentation/git-apply.txt +++ b/Documentation/git-apply.txt @@ -13,7 +13,7 @@ SYNOPSIS [--apply] [--no-add] [--build-fake-ancestor ] [-R | --reverse] [--allow-binary-replacement | --binary] [--reject] [-z] [-pNUM] [-CNUM] [--inaccurate-eof] [--cached] - [--whitespace=] + [--whitespace=] [--exclude=PATH] [--verbose] [...] DESCRIPTION @@ -135,25 +135,32 @@ discouraged. be useful when importing patchsets, where you want to exclude certain files or directories. ---whitespace=