* [PATCH 4/3] gitweb: Update README that gitweb works better with PATH_INFO
From: Jakub Narebski @ 2009-02-01 21:37 UTC (permalink / raw)
To: Giuseppe Bilotta; +Cc: git, Junio C Hamano, bug-CGI.pm
In-Reply-To: <1233365512-7130-4-git-send-email-giuseppe.bilotta@gmail.com>
One had to configure gitweb for it to find static files (stylesheets,
images) when using path_info URLs. Now that it is not necessary
thanks to adding BASE element to HTML head if needed, update README to
reflect this fact.
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
This is update of gitweb/README to fit state after Giuseppe patches.
There is a little problem either with terminology, or with CGI.pm
module itself. RFC 1808 "Relative Uniform Resource Locators" says
that:
http://git.example.com/cgi-bin/gitweb.cgi is absolute URL
/cgi-bin/gitweb.cgi is relative URL
but absolute path
gitweb.cgi is relative URL
and relative path
while CGI.pm (or, to be more exact, its url() method) says:
url() or url(-full) returns http://git.example.com/cgi-bin/gitweb.cgi
url(-absolute) returns /cgi-bin/gitweb.cgi
url(-relative) returns gitweb.cgi (or /cgi-bin/gitweb.cgi)
That is why I used "Full URL" for $my_url = $cgi->url();
and "absolute URL" (as before) for $my_uri = $cgi->url(-absolute);
in gitweb/README.
gitweb/README | 10 ++++------
1 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/gitweb/README b/gitweb/README
index 52ad88b..a9dc2e5 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -162,14 +162,12 @@ not include variables usually directly set during build):
$GITWEB_LIST during installation. If empty, $projectroot is used
to scan for repositories.
* $my_url, $my_uri
- URL and absolute URL of gitweb script; you might need to set those
- variables if you are using 'pathinfo' feature: see also below.
+ Full URL and absolute URL of gitweb script;
+ in earlier versions of gitweb you might have need to set those
+ variables, now there should be no need to do it.
* $home_link
Target of the home link on top of all pages (the first part of view
- "breadcrumbs"). By default set to absolute URI of a page; you might
- need to set it up to [base] gitweb URI if you use 'pathinfo' feature
- (alternative format of the URLs, with project name embedded directly
- in the path part of URL).
+ "breadcrumbs"). By default set to absolute URI of a page ($my_uri).
* @stylesheets
List of URIs of stylesheets (relative to base URI of a page). You
might specify more than one stylesheet, for example use gitweb.css
--
1.6.1
^ permalink raw reply related
* [PATCH v3] contrib git-resurrect: find traces of a branch name and resurrect it
From: Thomas Rast @ 2009-02-01 21:34 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Boyd Stephen Smith Jr.
In-Reply-To: <7vwschz2dc.fsf@gitster.siamese.dyndns.org>
Add a tool 'git-resurrect.sh <branch>' that tries to find traces of
the <branch> in the HEAD reflog and, optionally, all merge commits in
the repository. It can then resurrect the branch, pointing it at the
most recent of all candidate commits found.
Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
Junio C Hamano wrote:
> I hate to paint bikeshed, but -H "try-hard" looks somewhat unusual doesn't
> it? It sounds more like --all (find from all possible sources).
Why not. I had '-h' but then found out the hard way that it's
reserved...
> > +search_reflog_merges () {
> > + sed -n 's~^[^ ]* \([^ ]*\) .*\tmerge '"$1"':~\1~p' \
> > + < .git/logs/HEAD
> > +}
>
> The two commits both point at the HEAD that merges the other branch into,
> so this finds a merge commit that has the tip of target branch as its
> second parent. Is that really what you want?
Good point. Furthermore the sed expression was broken, it would not
remove the remainder of the line. Sadly it's not possible to insert
the reflog message and sha1 via --pretty=format, so I now use
rev-parse.
> Reading everything down to the root commit sounds like fun. rev-list
> gives you the output from newer to older so you may want to break out once
> you have found enough candidates.
>
> Anyway, if I were doing this script, I'd write this part like this without
> a shell loop:
>
> _x40="[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]"
> _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
>
> git rev-list --all --grep="Merge branch '$1'" \
> --pretty=tformat:"%H %P %s" |
> sed -ne "s/^$_x40 $_x40 \($_x40\) Merge .*/\1/p"
Nice trick. The same also works for scan_merge_targets() and gives it
a nice speed boost too. Unfortunately my sed-fu is not good enough to
figure out how to only print the first line (for resurrections from
pu, we expect there to be a single match). All uses of 'q' I could
come up with resulted in an early exit independent of the
substitutions. Appending '| head -n 1' does not seem to make any
difference either.
I also added the relative committer time to the candidate list, and
made it sort according to time; it seems somewhat more readable now.
contrib/git-resurrect.sh | 172 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 172 insertions(+), 0 deletions(-)
create mode 100755 contrib/git-resurrect.sh
diff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh
new file mode 100755
index 0000000..3a040ae
--- /dev/null
+++ b/contrib/git-resurrect.sh
@@ -0,0 +1,172 @@
+#!/bin/sh
+
+USAGE="[-a] [-r] [-m] [-t] [-n] [-b <newname>] <name>"
+LONG_USAGE="git-resurrect attempts to find traces of a branch tip
+called <name>, and tries to resurrect it. Currently, the reflog is
+searched for checkout messages, and with -r also merge messages. With
+-m and -t, the history of all refs is scanned for Merge <name> into
+other/Merge <other> into <name> (respectively) commit subjects, which
+is rather slow but allows you to resurrect other people's topic
+branches."
+
+OPTIONS_SPEC="\
+git resurrect $USAGE
+--
+b,branch= save branch as <newname> instead of <name>
+a,all same as -l -r -m -t
+l,reflog scan reflog for checkouts (enabled by default)
+r,reflog-merges scan for merges recorded in reflog
+m,merges scan for merges into other branches (slow)
+t,merge-targets scan for merges of other branches into <name>
+n,dry-run don't recreate the branch"
+
+. git-sh-setup
+
+search_reflog () {
+ sed -ne 's~^\([^ ]*\) .*\tcheckout: moving from '"$1"' .*~\1~p' \
+ < "$GIT_DIR"/logs/HEAD
+}
+
+search_reflog_merges () {
+ git rev-parse $(
+ sed -ne 's~^[^ ]* \([^ ]*\) .*\tmerge '"$1"':.*~\1^2~p' \
+ < "$GIT_DIR"/logs/HEAD
+ )
+}
+
+_x40="[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]"
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+
+search_merges () {
+ git rev-list --all --grep="Merge branch '$1'" \
+ --pretty=tformat:"%P %s" |
+ sed -ne "s/^$_x40 \($_x40\) Merge .*/\1/p"
+}
+
+search_merge_targets () {
+ git rev-list --all --grep="Merge branch '[^']*' into $branch\$" \
+ --pretty=tformat:"%H %s" --all |
+ sed -ne "s/^\($_x40\) Merge .*/\1/p"
+}
+
+dry_run=
+scan_reflog=t
+scan_reflog_merges=
+scan_merges=
+scan_merge_targets=
+new_name=
+
+while test "$#" != 0; do
+ case "$1" in
+ -b|--branch)
+ shift
+ new_name="$1"
+ ;;
+ -n|--dry-run)
+ dry_run=t
+ ;;
+ --no-dry-run)
+ dry_run=
+ ;;
+ -m|--merges)
+ scan_merges=t
+ ;;
+ --no-merges)
+ scan_merges=
+ ;;
+ -l|--reflog)
+ scan_reflog=t
+ ;;
+ --no-reflog)
+ scan_reflog=
+ ;;
+ -r|--reflog_merges)
+ scan_reflog_merges=t
+ ;;
+ --no-reflog_merges)
+ scan_reflog_merges=
+ ;;
+ -t|--merge-targets)
+ scan_merge_targets=t
+ ;;
+ --no-merge-targets)
+ scan_merge_targets=
+ ;;
+ -a|--all)
+ scan_reflog=t
+ scan_reflog_merges=t
+ scan_merges=t
+ scan_merge_targets=t
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ usage
+ ;;
+ esac
+ shift
+done
+
+test "$#" = 1 || usage
+
+all_strategies="$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets"
+if test -z "$all_strategies"; then
+ die "must enable at least one of -lrmt"
+fi
+
+branch="$1"
+test -z "$new_name" && new_name="$branch"
+
+if test ! -z "$scan_reflog"; then
+ if test -r "$GIT_DIR"/logs/HEAD; then
+ candidates="$(search_reflog $branch)"
+ else
+ die 'reflog scanning requested, but' \
+ '$GIT_DIR/logs/HEAD not readable'
+ fi
+fi
+if test ! -z "$scan_reflog_merges"; then
+ if test -r "$GIT_DIR"/logs/HEAD; then
+ candidates="$candidates $(search_reflog_merges $branch)"
+ else
+ die 'reflog scanning requested, but' \
+ '$GIT_DIR/logs/HEAD not readable'
+ fi
+fi
+if test ! -z "$scan_merges"; then
+ candidates="$candidates $(search_merges $branch)"
+fi
+if test ! -z "$scan_merge_targets"; then
+ candidates="$candidates $(search_merge_targets $branch)"
+fi
+
+candidates="$(git rev-parse $candidates | sort -u)"
+
+if test -z "$candidates"; then
+ hint=
+ test "z$all_strategies" != "ztttt" \
+ && hint=" (maybe try again with -a)"
+ die "no candidates for $branch found$hint"
+fi
+
+echo "** Candidates for $branch **"
+for cmt in $candidates; do
+ git --no-pager log --pretty=tformat:"%ct:%h [%cr] %s" --abbrev-commit -1 $cmt
+done \
+| sort -n | cut -d: -f2-
+
+newest="$(git rev-list -1 $candidates)"
+if test ! -z "$dry_run"; then
+ printf "** Most recent: "
+ git --no-pager log -1 --pretty=tformat:"%h %s" $newest
+elif ! git rev-parse --verify --quiet $new_name >/dev/null; then
+ printf "** Restoring $new_name to "
+ git --no-pager log -1 --pretty=tformat:"%h %s" $newest
+ git branch $new_name $newest
+else
+ printf "Most recent: "
+ git --no-pager log -1 --pretty=tformat:"%h %s" $newest
+ echo "** $new_name already exists, doing nothing"
+fi
--
1.6.1.2.495.gb8db2
^ permalink raw reply related
* Re: What's cooking in git.git (Jan 2009, #07; Wed, 28)
From: Junio C Hamano @ 2009-02-01 21:34 UTC (permalink / raw)
To: Kirill Smelkov; +Cc: git
In-Reply-To: <20090201174505.GA14181@roro3.zxlink>
Thanks, will apply.
^ permalink raw reply
* Re: [PATCH] Switch receive.denyCurrentBranch to "refuse"
From: Junio C Hamano @ 2009-02-01 21:33 UTC (permalink / raw)
To: Sam Vilain; +Cc: Nanako Shiraishi, Johannes Schindelin, Jay Soffian, git
In-Reply-To: <1233459475.17688.128.camel@maia.lan>
Sam Vilain <sam@vilain.net> writes:
> I just can't understand the
> resistance to this safety feature. People who encounter the bug can
> just change the setting and move on... it seems like an argument based
> on "principles", usually a sign that one has run out of actual
> arguments..
There is no resitance to any safety feature. The resistance is to
something entirely different.
The change we all share as the desired end result is to introduce a
different behaviour, even if it is a better one, that will deliberately
break people's working setup.
The end result being better does not justify breaking people's setup. You
need to help people whose setups will be broken prepare for such a change.
Perhaps you already forgot the fiasco after 1.6.0, which moved tons of
git-foo scripts out of the users' way. It resulted in a better layout in
the end, but we knew it would break people's working setup from the
beginning.
It was a change that many people argued for, saying that too many commands
in the path scared new people, that we thought we planned very carefully,
and that we thought we gave ample warning to existing users. Yet it
resulted in a huge fallout.
You probably forgot about it already, but that is because you weren't the
one who had to take the flak. I was, and I haven't forgotten.
The resistance is to an irresponsible transition strategy that causes pain
unnecessarily. We need to improve the situation incrementally, helping
new people a bit in one step while not hurting old people along the way,
aiming to help everybody more in the end.
One of the concluding comments after the 1.6.0 fiasco was that the
old-timers _heard about_ the upcoming change, but were too busy to stop
and think to realize that it is any urgent that they need to prepare for
it, and we should have warned them more actively. In the end, they didn't
mind the change itself per-se because we had an escape hatch (i.e. to
prepend the output from "git --exec-path" to the PATH in your script), but
the primary pain was having to adjust their setup on _our_ timetable, not
theirs.
Even k.org, which is one of the early adopters of new releases among the
larger sites (with larger proportion of old timers), has started using the
version with the "updating the branch you have checked out" warning (which
happened in v1.6.1) fairly recently, and during the discussion we noticed
that the warning didn't say we will be switching the default to "refuse"
any time soon. We haven't yet given them enough advance warning telling
them they will have to go running around flipping the bit in their
repositories. Dismissing the issue by saying "old-timers can simply flip
the configuration once" makes an irresponsible argument for repeating the
same mistake of 1.6.0. That is what I am resisting to.
I think the plan outlined in this thread would ease the transition in much
better way, and the patch I sent yesterday uses a deliberately loooooooong
warning message whose primary purpose is to be annoyingly obvious that the
users need to adjust their configuration now, so that the eventual change
in the default we will make won't inconvenience them.
I was reluctant to change the default for new repositories to refuse
during 1.6.2 timeframe, but I think with the attached patch on top of the
second patch I sent out earlier, it would force people choose before they
do any real damange to their repositories, and having it early would make
the overall transition plan smoother.
builtin-init-db.c | 2 +-
builtin-receive-pack.c | 26 ++++++++++++++++++++++++++
2 files changed, 27 insertions(+), 1 deletions(-)
diff --git c/builtin-init-db.c w/builtin-init-db.c
index 26c10cc..ea2765c 100644
--- c/builtin-init-db.c
+++ w/builtin-init-db.c
@@ -251,7 +251,7 @@ static int create_default_files(const char *template_path)
git_config_set("core.worktree", work_tree);
}
if (!reinit)
- git_config_set("receive.denyCurrentBranch", "refuse");
+ git_config_set("receive.denyCurrentBranch", "refuse-with-insn");
}
if (!reinit) {
diff --git c/builtin-receive-pack.c w/builtin-receive-pack.c
index f2c94fc..82d372f 100644
--- c/builtin-receive-pack.c
+++ w/builtin-receive-pack.c
@@ -16,6 +16,7 @@ enum deny_action {
DENY_IGNORE,
DENY_WARN,
DENY_REFUSE,
+ DENY_REFUSE_WITH_INSN,
};
static int deny_deletes = 0;
@@ -39,6 +40,8 @@ static enum deny_action parse_deny_action(const char *var, const char *value)
return DENY_WARN;
if (!strcasecmp(value, "refuse"))
return DENY_REFUSE;
+ if (!strcasecmp(value, "refuse-with-insn"))
+ return DENY_REFUSE_WITH_INSN;
}
if (git_config_bool(var, value))
return DENY_REFUSE;
@@ -236,6 +239,20 @@ static char *warn_unconfigured_deny_msg[] = {
"configuration variable set to either 'ignore' or 'warn'."
};
+static char *refuse_current_insn_msg[] = {
+ "By default, updating the current branch in a non-bare repository",
+ "is denied, because it will make the index and work tree inconsistent",
+ "with what you pushed, and will require 'git reset --hard' to match",
+ "the work tree to HEAD.",
+ "",
+ "You can set 'receive.denyCurrentBranch' configuration variable to",
+ "'ignore' or 'warn' in the repository to allow pushing into the",
+ "current branch of it; this is not recommended unless you really know",
+ "what you are doing.",
+ "",
+ "To squelch this message, you can set it to 'refuse'.",
+};
+
static void warn_unconfigured_deny(void)
{
int i;
@@ -243,6 +260,12 @@ static void warn_unconfigured_deny(void)
warning(warn_unconfigured_deny_msg[i]);
}
+static void refuse_current_insn(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(refuse_current_insn_msg); i++)
+ error(refuse_current_insn_msg[i]);
+}
static const char *update(struct command *cmd)
{
@@ -268,7 +291,10 @@ static const char *update(struct command *cmd)
warn_unconfigured_deny();
break;
case DENY_REFUSE:
+ case DENY_REFUSE_WITH_INSN:
error("refusing to update checked out branch: %s", name);
+ if (deny_current_branch == DENY_REFUSE_WITH_INSN)
+ refuse_current_insn();
return "branch is currently checked out";
}
}
^ permalink raw reply related
* [PATCH v2 4/4] Change current mailmap usage to do matching on both name and email of author/committer.
From: Marius Storm-Olsen @ 2009-02-01 20:59 UTC (permalink / raw)
To: git; +Cc: Marius Storm-Olsen
In-Reply-To: <cover.1233520945.git.marius@trolltech.com>
Signed-off-by: Marius Storm-Olsen <marius@trolltech.com>
---
Documentation/pretty-formats.txt | 2 +
builtin-blame.c | 50 ++++++++++++-------
builtin-shortlog.c | 22 +++++++--
pretty.c | 59 ++++++++++++-----------
t/t4203-mailmap.sh | 99 ++++++++++++++++++++++++++++++++++++++
5 files changed, 180 insertions(+), 52 deletions(-)
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 3d87d3e..28808b7 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -103,6 +103,7 @@ The placeholders are:
- '%an': author name
- '%aN': author name (respecting .mailmap)
- '%ae': author email
+- '%aE': author email (respecting .mailmap)
- '%ad': author date (format respects --date= option)
- '%aD': author date, RFC2822 style
- '%ar': author date, relative
@@ -111,6 +112,7 @@ The placeholders are:
- '%cn': committer name
- '%cN': committer name (respecting .mailmap)
- '%ce': committer email
+- '%cE': committer email (respecting .mailmap)
- '%cd': committer date
- '%cD': committer date, RFC2822 style
- '%cr': committer date, relative
diff --git a/builtin-blame.c b/builtin-blame.c
index 4b63775..c166ba1 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -1263,11 +1263,12 @@ struct commit_info
* Parse author/committer line in the commit object buffer
*/
static void get_ac_line(const char *inbuf, const char *what,
- int bufsz, char *person, const char **mail,
+ int person_len, char *person,
+ int mail_len, char *mail,
unsigned long *time, const char **tz)
{
int len, tzlen, maillen;
- char *tmp, *endp, *timepos;
+ char *tmp, *endp, *timepos, *mailpos;
tmp = strstr(inbuf, what);
if (!tmp)
@@ -1278,10 +1279,11 @@ static void get_ac_line(const char *inbuf, const char *what,
len = strlen(tmp);
else
len = endp - tmp;
- if (bufsz <= len) {
+ if (person_len <= len) {
error_out:
/* Ugh */
- *mail = *tz = "(unknown)";
+ *tz = "(unknown)";
+ strcpy(mail, *tz);
*time = 0;
return;
}
@@ -1304,9 +1306,10 @@ static void get_ac_line(const char *inbuf, const char *what,
*tmp = 0;
while (*tmp != ' ')
tmp--;
- *mail = tmp + 1;
+ mailpos = tmp + 1;
*tmp = 0;
maillen = timepos - tmp;
+ memcpy(mail, mailpos, maillen);
if (!mailmap.nr)
return;
@@ -1315,20 +1318,23 @@ static void get_ac_line(const char *inbuf, const char *what,
* mailmap expansion may make the name longer.
* make room by pushing stuff down.
*/
- tmp = person + bufsz - (tzlen + 1);
+ tmp = person + person_len - (tzlen + 1);
memmove(tmp, *tz, tzlen);
tmp[tzlen] = 0;
*tz = tmp;
- tmp = tmp - (maillen + 1);
- memmove(tmp, *mail, maillen);
- tmp[maillen] = 0;
- *mail = tmp;
-
/*
- * Now, convert e-mail using mailmap
+ * Now, convert both name and e-mail using mailmap
*/
- map_email(&mailmap, tmp + 1, person, tmp-person-1);
+ if(map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
+ /* Add a trailing '>' to email, since map_user returns plain emails
+ Note: It already has '<', since we replace from mail+1 */
+ mailpos = memchr(mail, '\0', mail_len);
+ if (mailpos && mailpos-mail < mail_len - 1) {
+ *mailpos = '>';
+ *(mailpos+1) = '\0';
+ }
+ }
}
static void get_commit_info(struct commit *commit,
@@ -1337,8 +1343,10 @@ static void get_commit_info(struct commit *commit,
{
int len;
char *tmp, *endp, *reencoded, *message;
- static char author_buf[1024];
- static char committer_buf[1024];
+ static char author_name[1024];
+ static char author_mail[1024];
+ static char committer_name[1024];
+ static char committer_mail[1024];
static char summary_buf[1024];
/*
@@ -1356,9 +1364,11 @@ static void get_commit_info(struct commit *commit,
}
reencoded = reencode_commit_message(commit, NULL);
message = reencoded ? reencoded : commit->buffer;
- ret->author = author_buf;
+ ret->author = author_name;
+ ret->author_mail = author_mail;
get_ac_line(message, "\nauthor ",
- sizeof(author_buf), author_buf, &ret->author_mail,
+ sizeof(author_name), author_name,
+ sizeof(author_mail), author_mail,
&ret->author_time, &ret->author_tz);
if (!detailed) {
@@ -1366,9 +1376,11 @@ static void get_commit_info(struct commit *commit,
return;
}
- ret->committer = committer_buf;
+ ret->committer = committer_name;
+ ret->committer_mail = committer_mail;
get_ac_line(message, "\ncommitter ",
- sizeof(committer_buf), committer_buf, &ret->committer_mail,
+ sizeof(committer_name), committer_name,
+ sizeof(committer_mail), committer_mail,
&ret->committer_time, &ret->committer_tz);
ret->summary = summary_buf;
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 04832a8..19975ce 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -40,6 +40,7 @@ static void insert_one_record(struct shortlog *log,
char *buffer, *p;
struct string_list_item *item;
char namebuf[1024];
+ char emailbuf[1024];
size_t len;
const char *eol;
const char *boemail, *eoemail;
@@ -51,7 +52,19 @@ static void insert_one_record(struct shortlog *log,
eoemail = strchr(boemail, '>');
if (!eoemail)
return;
- if (!map_email(&log->mailmap, boemail+1, namebuf, sizeof(namebuf))) {
+
+ /* copy author name to namebuf, to support matching on both name and email */
+ memcpy(namebuf, author, boemail - author);
+ len = boemail - author;
+ while(len > 0 && isspace(namebuf[len-1]))
+ len--;
+ namebuf[len] = 0;
+
+ /* copy email name to emailbuf, to allow email replacement as well */
+ memcpy(emailbuf, boemail+1, eoemail - boemail);
+ emailbuf[eoemail - boemail - 1] = 0;
+
+ if (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) {
while (author < boemail && isspace(*author))
author++;
for (len = 0;
@@ -67,8 +80,8 @@ static void insert_one_record(struct shortlog *log,
if (log->email) {
size_t room = sizeof(namebuf) - len - 1;
- int maillen = eoemail - boemail + 1;
- snprintf(namebuf + len, room, " %.*s", maillen, boemail);
+ int maillen = strlen(emailbuf);
+ snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf);
}
item = string_list_insert(namebuf, &log->list);
@@ -321,6 +334,5 @@ void shortlog_output(struct shortlog *log)
log->list.strdup_strings = 1;
string_list_clear(&log->list, 1);
- log->mailmap.strdup_strings = 1;
- string_list_clear(&log->mailmap, 1);
+ clear_mailmap(&log->mailmap);
}
diff --git a/pretty.c b/pretty.c
index cc460b5..e93e472 100644
--- a/pretty.c
+++ b/pretty.c
@@ -305,23 +305,14 @@ static char *logmsg_reencode(const struct commit *commit,
return out;
}
-static int mailmap_name(struct strbuf *sb, const char *email)
+static int mailmap_name(char *email, int email_len, char *name, int name_len)
{
static struct string_list *mail_map;
- char buffer[1024];
-
if (!mail_map) {
mail_map = xcalloc(1, sizeof(*mail_map));
- read_mailmap(mail_map, ".mailmap", NULL);
+ read_mailmap(mail_map, NULL, NULL);
}
-
- if (!mail_map->nr)
- return -1;
-
- if (!map_email(mail_map, email, buffer, sizeof(buffer)))
- return -1;
- strbuf_addstr(sb, buffer);
- return 0;
+ return mail_map->nr && map_user(mail_map, email, email_len, name, name_len);
}
static size_t format_person_part(struct strbuf *sb, char part,
@@ -332,6 +323,9 @@ static size_t format_person_part(struct strbuf *sb, char part,
int start, end, tz = 0;
unsigned long date = 0;
char *ep;
+ const char *name_start, *name_end, *mail_start, *mail_end, *msg_end = msg+len;
+ char person_name[1024];
+ char person_mail[1024];
/* advance 'end' to point to email start delimiter */
for (end = 0; end < len && msg[end] != '<'; end++)
@@ -345,25 +339,34 @@ static size_t format_person_part(struct strbuf *sb, char part,
if (end >= len - 2)
goto skip;
+ /* Seek for both name and email part */
+ name_start = msg;
+ name_end = msg+end;
+ while (name_end > name_start && isspace(*(name_end-1)))
+ name_end--;
+ mail_start = msg+end+1;
+ mail_end = mail_start;
+ while (mail_end < msg_end && *mail_end != '>')
+ mail_end++;
+ if (mail_end == msg_end)
+ goto skip;
+ end = mail_end-msg;
+
+ if (part == 'N' || part == 'E') { /* mailmap lookup */
+ strlcpy(person_name, name_start, name_end-name_start+1);
+ strlcpy(person_mail, mail_start, mail_end-mail_start+1);
+ mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));
+ name_start = person_name;
+ name_end = name_start + strlen(person_name);
+ mail_start = person_mail;
+ mail_end = mail_start + strlen(person_mail);
+ }
if (part == 'n' || part == 'N') { /* name */
- while (end > 0 && isspace(msg[end - 1]))
- end--;
- if (part != 'N' || !msg[end] || !msg[end + 1] ||
- mailmap_name(sb, msg + end + 2) < 0)
- strbuf_add(sb, msg, end);
+ strbuf_add(sb, name_start, name_end-name_start);
return placeholder_len;
}
- start = ++end; /* save email start position */
-
- /* advance 'end' to point to email end delimiter */
- for ( ; end < len && msg[end] != '>'; end++)
- ; /* do nothing */
-
- if (end >= len)
- goto skip;
-
- if (part == 'e') { /* email */
- strbuf_add(sb, msg + start, end - start);
+ if (part == 'e' || part == 'E') { /* email */
+ strbuf_add(sb, mail_start, mail_end-mail_start);
return placeholder_len;
}
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 57fd88e..271ef26 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -50,4 +50,103 @@ test_expect_success 'No mailmap files, but configured' '
test_cmp expect actual
'
+# Extended mailmap configurations should give us the following output for shortlog
+cat >expect <<\EOF
+A U Thor <author@example.com> (1):
+ initial
+
+Other Author <other@author.xx> (2):
+ third
+ fourth
+
+Santa Claus <santa.claus@northpole.xx> (2):
+ fifth
+ sixth
+
+Some Dude <some@dude.xx> (1):
+ second
+
+EOF
+
+test_expect_success 'Shortlog output (complex mapping)' '
+
+ echo two >>one &&
+ git add one &&
+ test_tick &&
+ git commit --author "nick1 <bugs@company.xx>" -m second &&
+
+ echo three >>one &&
+ git add one &&
+ test_tick &&
+ git commit --author "nick2 <bugs@company.xx>" -m third &&
+
+ echo four >>one &&
+ git add one &&
+ test_tick &&
+ git commit --author "nick2 <nick2@company.xx>" -m fourth &&
+
+ echo five >>one &&
+ git add one &&
+ test_tick &&
+ git commit --author "santa <me@company.xx>" -m fifth &&
+
+ echo six >>one &&
+ git add one &&
+ test_tick &&
+ git commit --author "claus <me@company.xx>" -m sixth &&
+
+ mkdir internal_mailmap &&
+ echo "Committed <committer@example.com>" > internal_mailmap/.mailmap &&
+ echo "Some Dude <some@dude.xx> nick1 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
+ echo "Other Author <other@author.xx> nick2 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
+ echo "Other Author <other@author.xx> <nick2@company.xx>" >> internal_mailmap/.mailmap &&
+ echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap &&
+ echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap &&
+
+ git shortlog -e >actual &&
+ test_cmp expect actual
+
+'
+
+# git log with --pretty format which uses the name and email mailmap placemarkers
+cat >expect <<\EOF
+Author claus <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author santa <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author nick2 <nick2@company.xx> maps to Other Author <other@author.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author nick2 <bugs@company.xx> maps to Other Author <other@author.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author nick1 <bugs@company.xx> maps to Some Dude <some@dude.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author A U Thor <author@example.com> maps to A U Thor <author@example.com>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+EOF
+
+test_expect_success 'Log output (complex mapping)' '
+ git log --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
+ test_cmp expect actual
+'
+
+# git blame
+cat >expect <<\EOF
+^3a2fdcb (A U Thor 2005-04-07 15:13:13 -0700 1) one
+e0f60492 (Some Dude 2005-04-07 15:14:13 -0700 2) two
+4db1a3df (Other Author 2005-04-07 15:15:13 -0700 3) three
+e3718380 (Other Author 2005-04-07 15:16:13 -0700 4) four
+25e4bf3d (Santa Claus 2005-04-07 15:17:13 -0700 5) five
+17c39712 (Santa Claus 2005-04-07 15:18:13 -0700 6) six
+EOF
+
+test_expect_success 'Blame output (complex mapping)' '
+ git blame one >actual &&
+ test_cmp expect actual
+'
+
test_done
--
1.6.1.2.257.g34f62
^ permalink raw reply related
* [PATCH v2 3/4] Add map_user() and clear_mailmap() to mailmap
From: Marius Storm-Olsen @ 2009-02-01 20:59 UTC (permalink / raw)
To: git; +Cc: Marius Storm-Olsen
In-Reply-To: <cover.1233520945.git.marius@trolltech.com>
map_user() allows to lookup and replace both email and name of a user, based on a new style mailmap file.
The possible mailmap definitions now are:
proper_name <commit_email> // Old style
proper_name <proper_email> <commit_email> // New style
proper_name <proper_email> commit_name <commit_email> // New style
map_email() operates the same as before, with the exception that it also will to try to match on a name passed in through the name return buffer.
clear_mailmap() is needed to now clear the more complex mailmap structure.
Signed-off-by: Marius Storm-Olsen <marius@trolltech.com>
---
Documentation/git-shortlog.txt | 58 +++++++++---
mailmap.c | 198 ++++++++++++++++++++++++++++++++++++----
mailmap.h | 4 +
3 files changed, 227 insertions(+), 33 deletions(-)
diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index cacbeea..d64dabf 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -50,20 +50,29 @@ FILES
If a file `.mailmap` exists at the toplevel of the repository, or at the
location pointed to by the log.mailmap configuration option,
-it is used to map an author email address to a canonical real name. This
-can be used to coalesce together commits by the same person where their
-name was spelled differently (whether with the same email address or
-not).
-
-Each line in the file consists, in this order, of the canonical real name
-of an author, whitespace, and an email address (enclosed by '<' and '>')
-to map to the name. Use hash '#' for comments, either on their own line,
-or after the email address.
-
-A canonical name may appear in more than one line, associated with
-different email addresses, but it doesn't make sense for a given address
-to appear more than once (if that happens, a later line overrides the
-earlier ones).
+it is used to map author and committer names and email addresses to
+canonical real names and email addresses.
+This can be used to coalesce together commits by the same person where
+their name and/or email address was spelled differently.
+
+In the simple form, each line in the file consists of the canonical real name
+of an author, whitespace, and an email address used in the commit
+(enclosed by '<' and '>') to map to the name. Thus, looks like this
+--
+ Proper Name <commit@email.xx>
+--
+
+The more complex forms are
+--
+ Proper Name <proper@email.xx> <commit@email.xx>
+--
+which allows mailmap to replace both the name and the email of a commit
+matching the specified commit email address. And
+--
+ Proper Name <proper@email.xx> Commit Name <commit@email.xx>
+--
+which allows mailmap to replace both the name and the email of a commit
+matching the specified commit name and email address.
So, for example, if your history contains commits by two authors, Jane
and Joe, whose names appear in the repository under several forms:
@@ -86,6 +95,27 @@ Jane Doe <jane@desktop.(none)>
Joe R. Developer <joe@random.com>
------------
+Now, suppose your repository contains commits from the following authors:
+
+------------
+nick1 <bugs@company.xx>
+nick2 <bugs@company.xx>
+nick2 <nick2@company.xx>
+santa <me@company.xx>
+claus <me@company.xx>
+------------
+
+Then, you might want a `.mailmap` file looking like:
+------------
+Some Dude <some@dude.xx> nick1 <bugs@company.xx>
+Other Author <other@author.xx> nick2 <bugs@company.xx>
+Other Author <other@author.xx> <nick2@company.xx>
+Santa Claus <santa.claus@northpole.xx> <me@company.xx>
+------------
+
+Use hash '#' for comments, either on their own line, or after the email address.
+
+
Author
------
Written by Jeff Garzik <jgarzik@pobox.com>
diff --git a/mailmap.c b/mailmap.c
index 32eab6c..937aa07 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -2,7 +2,87 @@
#include "string-list.h"
#include "mailmap.h"
+#define DEBUG_MAILMAP 0
+#if DEBUG_MAILMAP
+#define debug_mm(...) fprintf(stderr, __VA_ARGS__)
+#else
+inline void debug_mm(const char *format, ...) {}
+#endif
+
const char *git_log_mailmap;
+
+struct mailmap_info {
+ char *name;
+ char *email;
+};
+
+struct mailmap_entry {
+ /* name and email for the simple mail-only case */
+ char *name;
+ char *email;
+
+ /* name and email for the complex mail and name matching case */
+ struct string_list namemap;
+};
+
+void free_mailmap_info(void *p, const char *s)
+{
+ struct mailmap_info *mi = (struct mailmap_info *)p;
+ debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n", s, mi->name, mi->email);
+ free(mi->name);
+ free(mi->email);
+}
+
+void free_mailmap_entry(void *p, const char *s)
+{
+ struct mailmap_entry *me = (struct mailmap_entry *)p;
+ debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n", s, me->namemap.nr);
+ debug_mm("mailmap: - simple: '%s' <%s>\n", me->name, me->email);
+ free(me->name);
+ free(me->email);
+
+ me->namemap.strdup_strings = 1;
+ string_list_clear_func(&me->namemap, free_mailmap_info);
+}
+
+void add_mapping(struct string_list *map,
+ char *name1, char *email1, char *name2, char *email2)
+{
+ struct mailmap_entry *me;
+ int index = string_list_find_insert_index(map, email1, 1);
+ if (index < 0) {
+ /* mailmap entry exists, invert index value */
+ index = -1 - index;
+ } else {
+ /* create mailmap entry */
+ struct string_list_item *item = string_list_insert_at_index(index, email1, map);
+ item->util = xmalloc(sizeof(struct mailmap_entry));
+ memset(item->util, 0, sizeof(struct mailmap_entry));
+ }
+ me = (struct mailmap_entry *)map->items[index].util;
+
+ if (name2 == NULL) {
+ debug_mm("mailmap: adding (simple) entry for %s at index %d\n", email1, index);
+ /* Replace current name and new email for simple entry */
+ free(me->name);
+ free(me->email);
+ me->name = xstrdup(name1);
+ if (email2)
+ me->email = xstrdup(email2);
+ } else {
+ struct mailmap_info *mi = xmalloc(sizeof(struct mailmap_info));
+ debug_mm("mailmap: adding (complex) entry for %s at index %d\n", email1, index);
+ mi->name = xstrdup(name2);
+ if (email2)
+ mi->email = xstrdup(email2);
+ string_list_insert(name1, &me->namemap)->util = mi;
+ }
+
+ debug_mm("mailmap: '%s' <%s> -> '%s' <%s>\n",
+ name2 ? name1 : "", email1,
+ name2 ? name2 : name1, email2 ? email2 : "");
+}
+
int read_mailmap(struct string_list *map, const char *filename, char **repo_abbrev)
{
char buffer[1024];
@@ -17,8 +97,8 @@ int read_mailmap(struct string_list *map, const char *filename, char **repo_abbr
if (f == NULL)
return 1;
while (fgets(buffer, sizeof(buffer), f) != NULL) {
- char *end_of_name, *left_bracket, *right_bracket;
- char *name, *email;
+ char *end_of_name, *left_bracket1, *right_bracket1, *left_bracket2, *right_bracket2;
+ char *name1, *email1, *name2, *email2;
int i;
if (buffer[0] == '#') {
static const char abbrev[] = "# repo-abbrev:";
@@ -43,41 +123,95 @@ int read_mailmap(struct string_list *map, const char *filename, char **repo_abbr
}
continue;
}
- if ((left_bracket = strchr(buffer, '<')) == NULL)
+ /* Locate name and email */
+ if ((left_bracket1 = strchr(buffer, '<')) == NULL)
continue;
- if ((right_bracket = strchr(left_bracket + 1, '>')) == NULL)
+ if ((right_bracket1 = strchr(left_bracket1 + 1, '>')) == NULL)
continue;
- if (right_bracket == left_bracket + 1)
+ if (right_bracket1 == left_bracket1 + 1)
continue;
- for (end_of_name = left_bracket;
+ for (end_of_name = left_bracket1;
end_of_name != buffer && isspace(end_of_name[-1]);
end_of_name--)
; /* keep on looking */
if (end_of_name == buffer)
continue;
- name = xmalloc(end_of_name - buffer + 1);
- strlcpy(name, buffer, end_of_name - buffer + 1);
- email = xmalloc(right_bracket - left_bracket);
- for (i = 0; i < right_bracket - left_bracket - 1; i++)
- email[i] = tolower(left_bracket[i + 1]);
- email[right_bracket - left_bracket - 1] = '\0';
- string_list_insert(email, map)->util = name;
+ name1 = xmalloc(end_of_name - buffer + 1);
+ strlcpy(name1, buffer, end_of_name - buffer + 1);
+ email1 = xmalloc(right_bracket1 - left_bracket1);
+ for (i = 0; i < right_bracket1 - left_bracket1 - 1; i++)
+ email1[i] = tolower(left_bracket1[i + 1]);
+ email1[right_bracket1 - left_bracket1 - 1] = '\0';
+
+ /* Locate 2nd name and email. Possible mappings in mailmap file are:
+ * proper_name <commit_email>
+ * proper_name <proper_email> <commit_email>
+ * proper_name <proper_email> commit_name <commit_email>
+ */
+ do {
+ email2 = name2 = 0;
+ right_bracket1 += 1;
+ if ((left_bracket2 = strchr(right_bracket1, '<')) == NULL)
+ continue;
+ if ((right_bracket2 = strchr(left_bracket2 + 1, '>')) == NULL)
+ continue;
+ if (right_bracket2 == left_bracket2 + 1)
+ continue;
+ for (end_of_name = left_bracket2;
+ end_of_name != right_bracket1 && isspace(end_of_name[-1]);
+ end_of_name--)
+ ; /* keep on looking for name end */
+ for (;
+ end_of_name != right_bracket1 && isspace(right_bracket1[0]);
+ right_bracket1++)
+ ; /* keep on looking for name start */
+ if (end_of_name != right_bracket1) {
+ name2 = xmalloc(end_of_name - right_bracket1 + 1);
+ strlcpy(name2, right_bracket1, end_of_name - right_bracket1 + 1);
+ char *tmp = name1;
+ name1 = name2;
+ name2 = tmp;
+ }
+ email2 = xmalloc(right_bracket2 - left_bracket2);
+ for (i = 0; i < right_bracket2 - left_bracket2 - 1; i++)
+ email2[i] = tolower(left_bracket2[i + 1]);
+ email2[right_bracket2 - left_bracket2 - 1] = '\0';
+ char *tmp = email1;
+ email1 = email2;
+ email2 = tmp;
+ } while(0);
+
+ add_mapping(map, name1, email1, name2, email2);
}
fclose(f);
return 0;
}
-int map_email(struct string_list *map, const char *email, char *name, int maxlen)
+void clear_mailmap(struct string_list *map)
+{
+ debug_mm("mailmap: clearing %d entries...\n", map->nr);
+ map->strdup_strings = 1;
+ string_list_clear_func(map, free_mailmap_entry);
+ debug_mm("mailmap: cleared\n");
+}
+
+int map_user(struct string_list *map,
+ char *email, int maxlen_email, char *name, int maxlen_name)
{
char *p;
struct string_list_item *item;
+ struct mailmap_entry *me;
char buf[1024], *mailbuf;
int i;
- /* autocomplete common developers */
+ /* figure out space requirement for email */
p = strchr(email, '>');
- if (!p)
- return 0;
+ if (!p) {
+ /* email passed in might not be wrapped in <>, but end with a \0 */
+ p = memchr(email, '\0', maxlen_email);
+ if (p == 0)
+ return 0;
+ }
if (p - email + 1 < sizeof(buf))
mailbuf = buf;
else
@@ -87,13 +221,39 @@ int map_email(struct string_list *map, const char *email, char *name, int maxlen
for (i = 0; i < p - email; i++)
mailbuf[i] = tolower(email[i]);
mailbuf[i] = 0;
+
+ debug_mm("map_user: map '%s' <%s>\n", name, mailbuf);
item = string_list_lookup(mailbuf, map);
+ if (item != NULL) {
+ me = (struct mailmap_entry *)item->util;
+ if (me->namemap.nr) {
+ /* The item has multiple items, so we'll look up on name too */
+ /* If the name is not found, we choose the simple entry */
+ struct string_list_item *subitem = string_list_lookup(name, &me->namemap);
+ if (subitem)
+ item = subitem;
+ }
+ }
if (mailbuf != buf)
free(mailbuf);
if (item != NULL) {
- const char *realname = (const char *)item->util;
- strlcpy(name, realname, maxlen);
+ struct mailmap_info *mi = (struct mailmap_info *)item->util;
+ if (mi->email == NULL && mi->name == NULL) {
+ debug_mm("map_user: -- (no simple mapping)\n");
+ return 0;
+ }
+ if (maxlen_email && mi->email)
+ strlcpy(email, mi->email, maxlen_email);
+ if (maxlen_name && mi->name)
+ strlcpy(name, mi->name, maxlen_name);
+ debug_mm("map_user: to '%s' <%s>\n", name, mi->email ? email : mailbuf);
return 1;
}
+ debug_mm("map_user: --\n");
return 0;
}
+
+int map_email(struct string_list *map, const char *email, char *name, int maxlen)
+{
+ return map_user(map, (char *)email, 0, name, maxlen);
+}
diff --git a/mailmap.h b/mailmap.h
index 6e48f83..1b5ae69 100644
--- a/mailmap.h
+++ b/mailmap.h
@@ -2,6 +2,10 @@
#define MAILMAP_H
int read_mailmap(struct string_list *map, const char *filename, char **repo_abbrev);
+void clear_mailmap(struct string_list *map);
+
int map_email(struct string_list *mailmap, const char *email, char *name, int maxlen);
+int map_user(struct string_list *mailmap,
+ char *email, int maxlen_email, char *name, int maxlen_name);
#endif
--
1.6.1.2.257.g34f62
^ permalink raw reply related
* [PATCH v2 2/4] Add find_insert_index, insert_at_index and clear_func functions to string_list
From: Marius Storm-Olsen @ 2009-02-01 20:59 UTC (permalink / raw)
To: git; +Cc: Marius Storm-Olsen
In-Reply-To: <cover.1233520945.git.marius@trolltech.com>
string_list_find_insert_index() and string_list_insert_at_index() enables you to see if an item is in the string_list, and to insert at the appropriate index in the list, if not there.
This is usefull if you need to manipulate an existing item, if present, and insert a new item if not.
Future mailmap code will use this construct to enable complex (old_name, old_email) -> (new_name, new_email) lookups.
The string_list_clear_func() allows to call a custom cleanup function on each item in a string_list, which is useful is the util member points to a complex structure.
Signed-off-by: Marius Storm-Olsen <marius@trolltech.com>
---
string-list.c | 43 +++++++++++++++++++++++++++++++++++++++----
string-list.h | 9 +++++++++
2 files changed, 48 insertions(+), 4 deletions(-)
diff --git a/string-list.c b/string-list.c
index ddd83c8..15e14cf 100644
--- a/string-list.c
+++ b/string-list.c
@@ -26,10 +26,10 @@ static int get_entry_index(const struct string_list *list, const char *string,
}
/* returns -1-index if already exists */
-static int add_entry(struct string_list *list, const char *string)
+static int add_entry(int insert_at, struct string_list *list, const char *string)
{
- int exact_match;
- int index = get_entry_index(list, string, &exact_match);
+ int exact_match = 0;
+ int index = insert_at != -1 ? insert_at : get_entry_index(list, string, &exact_match);
if (exact_match)
return -1 - index;
@@ -53,7 +53,13 @@ static int add_entry(struct string_list *list, const char *string)
struct string_list_item *string_list_insert(const char *string, struct string_list *list)
{
- int index = add_entry(list, string);
+ return string_list_insert_at_index(-1, string, list);
+}
+
+struct string_list_item *string_list_insert_at_index(int insert_at,
+ const char *string, struct string_list *list)
+{
+ int index = add_entry(insert_at, list, string);
if (index < 0)
index = -1 - index;
@@ -68,6 +74,16 @@ int string_list_has_string(const struct string_list *list, const char *string)
return exact_match;
}
+int string_list_find_insert_index(const struct string_list *list, const char *string,
+ int negative_existing_index)
+{
+ int exact_match;
+ int index = get_entry_index(list, string, &exact_match);
+ if (exact_match)
+ index = -1 - (negative_existing_index ? index : 0);
+ return index;
+}
+
struct string_list_item *string_list_lookup(const char *string, struct string_list *list)
{
int exact_match, i = get_entry_index(list, string, &exact_match);
@@ -94,6 +110,25 @@ void string_list_clear(struct string_list *list, int free_util)
list->nr = list->alloc = 0;
}
+void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc)
+{
+ if (list->items) {
+ int i;
+ if (clearfunc) {
+ for (i = 0; i < list->nr; i++)
+ clearfunc(list->items[i].util, list->items[i].string);
+ }
+ if (list->strdup_strings) {
+ for (i = 0; i < list->nr; i++)
+ free(list->items[i].string);
+ }
+ free(list->items);
+ }
+ list->items = NULL;
+ list->nr = list->alloc = 0;
+}
+
+
void print_string_list(const char *text, const struct string_list *p)
{
int i;
diff --git a/string-list.h b/string-list.h
index 4d6a705..d32ba05 100644
--- a/string-list.h
+++ b/string-list.h
@@ -15,9 +15,18 @@ struct string_list
void print_string_list(const char *text, const struct string_list *p);
void string_list_clear(struct string_list *list, int free_util);
+/* Use this function to call a custom clear function on each util pointer */
+/* The string associated with the util pointer is passed as the second argument */
+typedef void (*string_list_clear_func_t)(void *p, const char *str);
+void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc);
+
/* Use these functions only on sorted lists: */
int string_list_has_string(const struct string_list *list, const char *string);
+int string_list_find_insert_index(const struct string_list *list, const char *string,
+ int negative_existing_index);
struct string_list_item *string_list_insert(const char *string, struct string_list *list);
+struct string_list_item *string_list_insert_at_index(int insert_at,
+ const char *string, struct string_list *list);
struct string_list_item *string_list_lookup(const char *string, struct string_list *list);
/* Use these functions only on unsorted lists: */
--
1.6.1.2.257.g34f62
^ permalink raw reply related
* [PATCH v2 1/4] Add log.mailmap as configurational option for mailmap location
From: Marius Storm-Olsen @ 2009-02-01 20:59 UTC (permalink / raw)
To: git; +Cc: Marius Storm-Olsen
In-Reply-To: <cover.1233520945.git.marius@trolltech.com>
This allows us to override a repo mailmap file, or to use
mailmap files elsewhere than the repository root.
Should the log.mailmap file not be found, it falls back to ".mailmap".
Signed-off-by: Marius Storm-Olsen <marius@trolltech.com>
---
Documentation/config.txt | 9 +++++++
Documentation/git-shortlog.txt | 3 +-
builtin-blame.c | 2 +-
builtin-shortlog.c | 3 +-
cache.h | 1 +
config.c | 10 +++++++
mailmap.c | 9 ++++++-
t/t4203-mailmap.sh | 53 ++++++++++++++++++++++++++++++++++++++++
8 files changed, 86 insertions(+), 4 deletions(-)
create mode 100755 t/t4203-mailmap.sh
diff --git a/Documentation/config.txt b/Documentation/config.txt
index e2b8775..02c7b4c 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1012,6 +1012,15 @@ log.showroot::
Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
normally hide the root commit will now show it. True by default.
+log.mailmap::
+ Overrides the default location of the mailmap file. The default
+ mailmap location is .mailmap in the root of the repository.
+ The new location of the mailmap file may be in a repository
+ subdirectory, or somewhere outside of the repository itself.
+ If git can't find the file, it will try to load the default
+ mailmap location instead.
+ See linkgit:git-shortlog[1] and linkgit:git-blame[1].
+
man.viewer::
Specify the programs that may be used to display help in the
'man' format. See linkgit:git-help[1].
diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index 8f7c0e2..cacbeea 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -48,7 +48,8 @@ OPTIONS
FILES
-----
-If a file `.mailmap` exists at the toplevel of the repository,
+If a file `.mailmap` exists at the toplevel of the repository, or at the
+location pointed to by the log.mailmap configuration option,
it is used to map an author email address to a canonical real name. This
can be used to coalesce together commits by the same person where their
name was spelled differently (whether with the same email address or
diff --git a/builtin-blame.c b/builtin-blame.c
index aae14ef..4b63775 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -2394,7 +2394,7 @@ parse_done:
die("reading graft file %s failed: %s",
revs_file, strerror(errno));
- read_mailmap(&mailmap, ".mailmap", NULL);
+ read_mailmap(&mailmap, NULL, NULL);
if (!incremental)
setup_pager();
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 5f9f3f0..04832a8 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -219,7 +219,7 @@ void shortlog_init(struct shortlog *log)
{
memset(log, 0, sizeof(*log));
- read_mailmap(&log->mailmap, ".mailmap", &log->common_repo_prefix);
+ read_mailmap(&log->mailmap, NULL, &log->common_repo_prefix);
log->list.strdup_strings = 1;
log->wrap = DEFAULT_WRAPLEN;
@@ -248,6 +248,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
struct parse_opt_ctx_t ctx;
prefix = setup_git_directory_gently(&nongit);
+ git_config(git_default_config, NULL);
shortlog_init(&log);
init_revisions(&rev, prefix);
parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH |
diff --git a/cache.h b/cache.h
index 45e713e..3eef7ea 100644
--- a/cache.h
+++ b/cache.h
@@ -867,6 +867,7 @@ extern int user_ident_explicitly_given;
extern const char *git_commit_encoding;
extern const char *git_log_output_encoding;
+extern const char *git_log_mailmap;
/* IO helper functions */
extern void maybe_flush_or_die(FILE *, const char *);
diff --git a/config.c b/config.c
index 790405a..9ebcbbe 100644
--- a/config.c
+++ b/config.c
@@ -565,6 +565,13 @@ static int git_default_branch_config(const char *var, const char *value)
return 0;
}
+static int git_default_log_config(const char *var, const char *value)
+{
+ if (!strcmp(var, "log.mailmap"))
+ return git_config_string(&git_log_mailmap, var, value);
+ return 0;
+}
+
int git_default_config(const char *var, const char *value, void *dummy)
{
if (!prefixcmp(var, "core."))
@@ -579,6 +586,9 @@ int git_default_config(const char *var, const char *value, void *dummy)
if (!prefixcmp(var, "branch."))
return git_default_branch_config(var, value);
+ if (!prefixcmp(var, "log."))
+ return git_default_log_config(var, value);
+
if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
pager_use_color = git_config_bool(var,value);
return 0;
diff --git a/mailmap.c b/mailmap.c
index 88fc6f3..32eab6c 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -2,11 +2,18 @@
#include "string-list.h"
#include "mailmap.h"
+const char *git_log_mailmap;
int read_mailmap(struct string_list *map, const char *filename, char **repo_abbrev)
{
char buffer[1024];
- FILE *f = fopen(filename, "r");
+ FILE *f = NULL;
+ if (filename)
+ f = fopen(filename, "r");
+ if (f == NULL && git_log_mailmap)
+ f = fopen(git_log_mailmap, "r");
+ if (f == NULL)
+ f = fopen(".mailmap", "r");
if (f == NULL)
return 1;
while (fgets(buffer, sizeof(buffer), f) != NULL) {
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
new file mode 100755
index 0000000..57fd88e
--- /dev/null
+++ b/t/t4203-mailmap.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description='.mailmap configurations'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo one >one &&
+ git add one &&
+ test_tick &&
+ git commit -m initial
+'
+
+test_expect_success 'No mailmap' '
+ git shortlog >actual &&
+ echo "A U Thor (1):" >expect; echo " initial" >>expect; echo >>expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'default .mailmap' '
+ echo "Repo Guy <author@example.com>" > .mailmap &&
+ git shortlog >actual &&
+ echo "Repo Guy (1):" >expect; echo " initial" >>expect; echo >>expect &&
+ test_cmp expect actual
+'
+
+# Using a mailmap file in a subdirectory of the repo here, but
+# could just as well have been a file outside of the repository
+test_expect_success 'log.mailmap set' '
+ mkdir internal_mailmap &&
+ echo "Internal Guy <author@example.com>" > internal_mailmap/.mailmap &&
+ git config log.mailmap internal_mailmap/.mailmap &&
+ git shortlog >actual &&
+ echo "Internal Guy (1):" >expect; echo " initial" >>expect; echo >>expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log.mailmap file non-existant' '
+ rm internal_mailmap/.mailmap &&
+ rmdir internal_mailmap &&
+ git shortlog >actual &&
+ echo "Repo Guy (1):" >expect; echo " initial" >>expect; echo >>expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'No mailmap files, but configured' '
+ rm .mailmap &&
+ git shortlog >actual &&
+ echo "A U Thor (1):" >expect; echo " initial" >>expect; echo >>expect &&
+ test_cmp expect actual
+'
+
+test_done
--
1.6.1.2.257.g34f62
^ permalink raw reply related
* [PATCH v2 0/4] Extend mailmap functionality
From: Marius Storm-Olsen @ 2009-02-01 20:59 UTC (permalink / raw)
To: git; +Cc: Marius Storm-Olsen
v2: changes since v1
--------------------
* Folded in documentation fixup from patch 4 into patch 3.
This patch series extends the mailmap functionality to:
1) Allow the mailmap file in any location (also outside repo)
2) Enable mailmap to match on both Name and Email
So, why would this be a good thing?
2) Lets you replace both name and email of an author/committer, based
on a name and/or email. So, should you have done commits with faulty
address, or if an old email simply isn't valid anymore, you can add
a mapping for that to replace it. So, the old style mapping is
Proper Name <commit@email.xx>
while this patch series adds support for
Proper Name <proper@email.xx> <commit@email.xx>
Proper Name <proper@email.xx> Commit Name <commit@email.xx>
1) Lets you keep a private mailmap file, which is not distributed with
your repository.
This extended mapping is necessary when a company wants to have their
repositories open to the public, but needs to protect the identities
of the developers. It enables you to only show nicks and standardized
emails, like 'Dev123 <bugs@company.xx>' in the public repo, but by
using an private mailmap file, map the name back to
'John Doe <john.doe@company.xx>' inside the company.
Patch serie applies cleanly on master branch, and test run shows no
regressions.
Marius Storm-Olsen (4):
Add log.mailmap as configurational option for mailmap location
Add find_insert_index, insert_at_index and clear_func functions to
string_list
Add map_user() and clear_mailmap() to mailmap
Change current mailmap usage to do matching on both name and email of
author/committer.
Documentation/config.txt | 9 ++
Documentation/git-shortlog.txt | 61 +++++++++---
Documentation/pretty-formats.txt | 2 +
builtin-blame.c | 52 ++++++----
builtin-shortlog.c | 25 ++++-
cache.h | 1 +
config.c | 10 ++
mailmap.c | 207 ++++++++++++++++++++++++++++++++++----
mailmap.h | 4 +
pretty.c | 59 ++++++-----
string-list.c | 43 +++++++-
string-list.h | 9 ++
t/t4203-mailmap.sh | 152 ++++++++++++++++++++++++++++
13 files changed, 541 insertions(+), 93 deletions(-)
create mode 100755 t/t4203-mailmap.sh
^ permalink raw reply
* Re: [EGIT PATCH] Resurrect group filtering options in history pane
From: Robin Rosenberg @ 2009-02-01 20:43 UTC (permalink / raw)
To: spearce; +Cc: git
In-Reply-To: <1233520578-10453-1-git-send-email-robin.rosenberg@dewire.com>
söndag 01 februari 2009 21:36:18 skrev jag:
> This commit restores the ability to filter on not only the selected
> resource but instead all changes in the same folder, same project
> or same repository.
> The filtering levels supported are Resource (no button
> pessed), Folder, Project and Repository. Only the highest level has any
> effect.
Omg... I just told people not to repeat themselves...
> The flags are persistent between eclipse sessions in the same
> workspace.
-- robin
^ permalink raw reply
* [EGIT PATCH] Resurrect group filtering options in history pane
From: Robin Rosenberg @ 2009-02-01 20:36 UTC (permalink / raw)
To: spearce; +Cc: git, Robin Rosenberg
This commit restores the ability to filter on not only the selected
resource but instead all changes in the same folder, same project
or same repository. The filtering levels supported are Resource (no button
pessed), Folder, Project and Repository. Only the highest level has any
effect. The flags are persistent between eclipse sessions in the same
workspace.
Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>
---
.../egit/ui/internal/history/GitHistoryPage.java | 108 +++++++++++++++++++-
1 files changed, 104 insertions(+), 4 deletions(-)
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/GitHistoryPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/GitHistoryPage.java
index d718cd7..4e95df4 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/GitHistoryPage.java
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/history/GitHistoryPage.java
@@ -96,7 +96,7 @@
/**
* Determine if the input can be shown in this viewer.
- *
+ *
* @param object
* an object that is hopefully of type ResourceList or IResource,
* but may be anything (including null).
@@ -188,6 +188,18 @@ private static boolean typeOk(final IResource object) {
*/
private List<String> pathFilters;
+ private static final String PREF_SHOWALLREPOVERSIONS = "org.spearce.egit.ui.githistorypage.showallrepoversions";
+
+ private static final String PREF_SHOWALLPROJECTVERSIONS = "org.spearce.egit.ui.githistorypage.showallprojectversions";
+
+ private static final String PREF_SHOWALLFOLDERVERSIONS = "org.spearce.egit.ui.githistorypage.showallfolderversions";
+
+ private boolean showAllProjectVersions;
+
+ private boolean showAllFolderVersions;
+
+ private boolean showAllRepoVersions;
+
/**
* The selection provider tracks the selected revisions for the context menu
*/
@@ -236,9 +248,85 @@ public void createControl(final Composite parent) {
attachContextMenu(fileViewer.getControl());
layout();
+ showAllProjectVersions = Activator.getDefault().getPreferenceStore()
+ .getBoolean(PREF_SHOWALLPROJECTVERSIONS);
+ showAllFolderVersions = Activator.getDefault().getPreferenceStore()
+ .getBoolean(PREF_SHOWALLFOLDERVERSIONS);
+ showAllRepoVersions = Activator.getDefault().getPreferenceStore()
+ .getBoolean(PREF_SHOWALLREPOVERSIONS);
+
+ Action showAllRepoVersionsAction = new Action("R") {
+ public void run() {
+ cancelRefreshJob();
+ setShowAllRepoVersions(isChecked());
+ cancelRefreshJob();
+ }
+ };
+ showAllRepoVersionsAction
+ .setToolTipText("Show all versions for the repository containing the resource");
+ showAllRepoVersionsAction.setChecked(isShowAllRepoVersions());
+ getSite().getActionBars().getToolBarManager().add(
+ showAllRepoVersionsAction);
+
+ Action showAllProjectVersionsAction = new Action("P") {
+ public void run() {
+ cancelRefreshJob();
+ setShowAllProjectVersions(isChecked());
+ inputSet();
+ }
+ };
+ showAllProjectVersionsAction
+ .setToolTipText("Show all versions for the project containing the resource");
+ showAllProjectVersionsAction.setChecked(isShowAllProjectVersions());
+ getSite().getActionBars().getToolBarManager().add(
+ showAllProjectVersionsAction);
+
+ Action showAllFolderVersionsAction = new Action("F") {
+ public void run() {
+ cancelRefreshJob();
+ setShowAllFolderVersion(isChecked());
+ inputSet();
+ }
+ };
+ showAllFolderVersionsAction
+ .setToolTipText("Show all versions for the folder containing the resource");
+ showAllFolderVersionsAction.setChecked(isShowAllFolderVersions());
+ getSite().getActionBars().getToolBarManager().add(
+ showAllFolderVersionsAction);
+
Repository.addAnyRepositoryChangedListener(this);
}
+ /* private */boolean isShowAllRepoVersions() {
+ return showAllRepoVersions;
+ }
+
+ void setShowAllRepoVersions(boolean showAllRepoVersions) {
+ this.showAllRepoVersions = showAllRepoVersions;
+ Activator.getDefault().getPreferenceStore().setValue(
+ PREF_SHOWALLREPOVERSIONS, showAllRepoVersions);
+ }
+
+ /* private */boolean isShowAllProjectVersions() {
+ return showAllProjectVersions;
+ }
+
+ void setShowAllProjectVersions(boolean showAllProjectVersions) {
+ this.showAllProjectVersions = showAllProjectVersions;
+ Activator.getDefault().getPreferenceStore().setValue(
+ PREF_SHOWALLPROJECTVERSIONS, showAllProjectVersions);
+ }
+
+ /* private */boolean isShowAllFolderVersions() {
+ return showAllFolderVersions;
+ }
+
+ void setShowAllFolderVersion(boolean showAllFolderVersions) {
+ this.showAllFolderVersions = showAllFolderVersions;
+ Activator.getDefault().getPreferenceStore().setValue(
+ PREF_SHOWALLFOLDERVERSIONS, showAllFolderVersions);
+ }
+
private Runnable refschangedRunnable;
public void refsChanged(final RefsChangedEvent e) {
@@ -566,9 +654,21 @@ public boolean inputSet() {
else if (db != map.getRepository())
return false;
- final String name = map.getRepoRelativePath(r);
- if (name != null && name.length() > 0)
- paths.add(name);
+ if (isShowAllFolderVersions()) {
+ final String name = map.getRepoRelativePath(r.getParent());
+ if (name != null && name.length() > 0)
+ paths.add(name);
+ } else if (isShowAllProjectVersions()) {
+ final String name = map.getRepoRelativePath(r.getProject());
+ if (name != null && name.length() > 0)
+ paths.add(name);
+ } else if (isShowAllRepoVersions()) {
+ // nothing
+ } else {
+ final String name = map.getRepoRelativePath(r);
+ if (name != null && name.length() > 0)
+ paths.add(name);
+ }
}
if (db == null)
--
1.6.1.285.g35d8b
^ permalink raw reply related
* [RFC PATCH] add -p: prompt for single characters
From: Thomas Rast @ 2009-02-01 20:35 UTC (permalink / raw)
To: git; +Cc: Jeff King, Suraj N. Kurapati
Use Term::ReadKey, if available, to let the user answer add -p's
prompts by pressing a single key. The 'g' command is the only one
that takes an argument, but can easily cope since it'll just offer a
choice of chunks. We're not doing the same in the main 'add -i'
interface because file selection etc. may expect several characters.
Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
I wrote this on the train today, but it turns out a similar idea was
already around earlier:
http://thread.gmane.org/gmane.comp.version-control.git/100146
I can't find the v4 promised there, so I assume Suraj dropped it.
It would indeed be nice if we could apply the same to the main 'add
-i' loop, and I played with the code to do so for a while. Most of
the mechanisms required seem to be in place; it already computes
shortest unique prefixes for the inputs given, so we could just grab
the one-character prefixes from there.
However, what to do when the user entered a letter that is known to be
part of several prefixes? If we offer to enter the rest of the line,
e.g., 'return $key.<STDIN>', the user can't backspace away the
"existing" input if he decides to do something else instead. Perhaps
readline could prime the input line with the letter, but I can't find
any such feature in the Term::ReadLine docs. Implementation ideas are
obviously very welcome.
git-add--interactive.perl | 35 ++++++++++++++++++++++++++++++++---
1 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index ca60356..0633eca 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -33,6 +33,14 @@ my ($diff_new_color) =
my $normal_color = $repo->get_color("", "reset");
+my $use_readkey = 0;
+my %control_keys;
+eval {
+ use Term::ReadKey;
+ $use_readkey = 1;
+ %control_keys = GetControlChars;
+};
+
sub colored {
my $color = shift;
my $string = join("", @_);
@@ -758,11 +766,32 @@ sub diff_applies {
return close $fh;
}
+sub _restore_terminal_and_die {
+ ReadMode 'restore';
+ print "\n";
+ exit 1;
+}
+
+sub prompt_single_character {
+ if ($use_readkey) {
+ local $SIG{TERM} = \&_restore_terminal_and_die;
+ local $SIG{INT} = \&_restore_terminal_and_die;
+ ReadMode 'cbreak';
+ my $key = ReadKey 0;
+ ReadMode 'restore';
+ print "$key" if defined $key;
+ print "\n";
+ return $key;
+ } else {
+ return <STDIN>;
+ }
+}
+
sub prompt_yesno {
my ($prompt) = @_;
while (1) {
print colored $prompt_color, $prompt;
- my $line = <STDIN>;
+ my $line = prompt_single_character;
return 0 if $line =~ /^n/i;
return 1 if $line =~ /^y/i;
}
@@ -892,7 +921,7 @@ sub patch_update_file {
print @{$mode->{DISPLAY}};
print colored $prompt_color,
"Stage mode change [y/n/a/d/?]? ";
- my $line = <STDIN>;
+ my $line = prompt_single_character;
if ($line =~ /^y/i) {
$mode->{USE} = 1;
last;
@@ -965,7 +994,7 @@ sub patch_update_file {
print;
}
print colored $prompt_color, "Stage this hunk [y/n/a/d$other/?]? ";
- my $line = <STDIN>;
+ my $line = prompt_single_character;
if ($line) {
if ($line =~ /^y/i) {
$hunk[$ix]{USE} = 1;
--
tg: (a34a9db..) t/add-p-readkey (depends on: origin/master)
^ permalink raw reply related
* [PATCH/RFC v2 7/7] unpack-trees.c: introduce schedule_dir_for_removal()
From: Kjetil Barvik @ 2009-02-01 20:23 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kjetil Barvik
In-Reply-To: <cover.1233499703.git.barvik@broadpark.no>
Currently inside unlink_entry() if we get a successful removal of one
file with unlink(), we try to remove the leading directories each and
every time. So if one directory containing 200 files is moved to an
other location we get 199 failed calls to rmdir() and 1 successful
call.
To fix this and to avoid some unnecessary calls to rmdir(), we only
schedule each directory for removal and we wait much longer before we
do the actually call to rmdir().
Since the unlink_entry() function is called with alphabetically sorted
names, this new function end up being very effective to avoid
unnecessary calls to rmdir(). In some cases over 95% of all calls to
rmdir() is removed with this patch.
Signed-off-by: Kjetil Barvik <barvik@broadpark.no>
---
I noticed that I had to copy-and-paste almost all lines from the
longest_match_lstat_cache() in symlinks.c into this new function, so
maybe I should do the following:
Move the 3 new functions below inside the same file (dir.c?) as the
lstat_cache() functions, rename longest_match_lstat_cache() to
longest_dir_match(), do some cleanup/fix'es, and let it be called
from the 3 functions which currently need it.
Good idea?
unpack-trees.c | 97 ++++++++++++++++++++++++++++++++++++++++++--------------
1 files changed, 73 insertions(+), 24 deletions(-)
diff --git a/unpack-trees.c b/unpack-trees.c
index e547282..5c12eca 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -52,36 +52,84 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK);
}
-/* Unlink the last component and attempt to remove leading
- * directories, in case this unlink is the removal of the
- * last entry in the directory -- empty directories are removed.
+static char path_buf[PATH_MAX];
+static unsigned int path_len;
+
+static void do_remove_scheduled_dirs(unsigned int new_len)
+{
+ while (path_len > new_len) {
+ path_buf[path_len] = '\0';
+ if (rmdir(path_buf))
+ break;
+ do {
+ path_len--;
+ } while (path_len > new_len && path_buf[path_len] != '/');
+ }
+ path_len = new_len;
+ return;
+}
+
+static void schedule_dir_for_removal(const char *name, unsigned int len)
+{
+ unsigned int i = 0, match_len = 0, max_len, last_slash;
+
+ /* Find longest dir-match between 'name' and 'path' */
+ max_len = len < path_len ? len : path_len;
+ while (i < max_len && name[i] == path_buf[i]) {
+ if (name[i] == '/')
+ match_len = i;
+ i++;
+ }
+ if (i >= max_len && ((len > path_len && name[path_len] == '/') ||
+ (len < path_len && path_buf[len] == '/') ||
+ (len == path_len)))
+ match_len = i;
+
+ /* Find the last slash inside 'name' */
+ last_slash = match_len;
+ while (i < len) {
+ if (name[i] == '/')
+ last_slash = i;
+ i++;
+ }
+
+ /*
+ * If we are about to go down the directory tree, we check if
+ * we must first go upwards the tree, such that we then can
+ * remove possible empty directories as we go upwards.
+ */
+ if (match_len < last_slash && match_len < path_len)
+ do_remove_scheduled_dirs(match_len);
+ /*
+ * If we go deeper down the directory tree, we only need to
+ * save the new path components as we go down.
+ */
+ if (match_len < last_slash) {
+ memmove(&path_buf[match_len], &name[match_len],
+ last_slash - match_len);
+ path_len = last_slash;
+ }
+ return;
+}
+
+#define ALL_DIRS 0
+static void remove_scheduled_dirs(void)
+{
+ do_remove_scheduled_dirs(ALL_DIRS);
+ return;
+}
+
+/*
+ * Unlink the last component and schedule the leading directories for
+ * removal, such that empty directories get removed.
*/
static void unlink_entry(struct cache_entry *ce)
{
- char *cp, *prev;
- char *name = ce->name;
-
if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name))
return;
- if (unlink(name))
+ if (unlink(ce->name))
return;
- prev = NULL;
- while (1) {
- int status;
- cp = strrchr(name, '/');
- if (prev)
- *prev = '/';
- if (!cp)
- break;
-
- *cp = 0;
- status = rmdir(name);
- if (status) {
- *cp = '/';
- break;
- }
- prev = cp;
- }
+ schedule_dir_for_removal(ce->name, ce_namelen(ce));
}
static struct checkout state;
@@ -117,6 +165,7 @@ static int check_updates(struct unpack_trees_options *o)
continue;
}
}
+ remove_scheduled_dirs();
for (i = 0; i < index->cache_nr; i++) {
struct cache_entry *ce = index->cache[i];
--
1.6.1.349.g99fa5
^ permalink raw reply related
* [PATCH/RFC v2 6/7] lstat_cache(): print a warning if doing ping-pong between cache types
From: Kjetil Barvik @ 2009-02-01 20:23 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kjetil Barvik
In-Reply-To: <cover.1233499703.git.barvik@broadpark.no>
This is a debug patch which is only to be used while the lstat_cache()
is in the test stage, and should be removed/reverted before the final
relase.
I think it should be useful to catch these warnings, as I it could be
an indication of that the cache would not be very effective if it is
doing ping-pong by switching between different cache types too many
times.
Also, if someone is experimenting with the lstat_cache(), this patch
will maybe be useful while debugging.
If someone is able to trigger the warning, then send a mail to the GIT
mailing list, containing the first 15 lines of the warning, and a
short description of the GIT commands to trigger the warnings.
I hope someone is willing to use this patch for a while, to be able to
catch possible ping-pong's.
Signed-off-by: Kjetil Barvik <barvik@broadpark.no>
---
symlinks.c | 23 +++++++++++++++++++++++
1 files changed, 23 insertions(+), 0 deletions(-)
diff --git a/symlinks.c b/symlinks.c
index ae57e56..fe4b188 100644
--- a/symlinks.c
+++ b/symlinks.c
@@ -51,6 +51,11 @@ static inline void reset_lstat_cache(void)
*/
}
+#define SWITCHES_BEFORE_WARNING 10
+static unsigned int cache_switches, number_of_warnings;
+static unsigned int current_cache_func, last_cache_func;
+static unsigned int total_calls;
+
#define FL_DIR (1 << 0)
#define FL_NOENT (1 << 1)
#define FL_SYMLINK (1 << 2)
@@ -77,6 +82,7 @@ static int lstat_cache(int len, const char *name,
int match_flags, ret_flags, save_flags, max_len, ret;
struct stat st;
+ total_calls++;
if (cache.track_flags != track_flags ||
cache.prefix_len_stat_func != prefix_len_stat_func) {
/*
@@ -88,6 +94,17 @@ static int lstat_cache(int len, const char *name,
cache.track_flags = track_flags;
cache.prefix_len_stat_func = prefix_len_stat_func;
match_len = last_slash = 0;
+ cache_switches++;
+ if (cache_switches > SWITCHES_BEFORE_WARNING) {
+ if (number_of_warnings < 10 || number_of_warnings % 1000 == 0)
+ printf("warning from %s:%d cache_switches:%u > %u "\
+ "(current:%u last:%u total:%u)\n",
+ __FILE__, __LINE__,
+ cache_switches, SWITCHES_BEFORE_WARNING,
+ current_cache_func, last_cache_func,
+ total_calls);
+ number_of_warnings++;
+ }
} else {
/*
* Check to see if we have a match from the cache for
@@ -214,6 +231,8 @@ void clear_lstat_cache(void)
*/
int has_symlink_leading_path(int len, const char *name)
{
+ last_cache_func = current_cache_func;
+ current_cache_func = 1;
return lstat_cache(len, name,
FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
FL_SYMLINK;
@@ -225,6 +244,8 @@ int has_symlink_leading_path(int len, const char *name)
*/
int has_symlink_or_noent_leading_path(int len, const char *name)
{
+ last_cache_func = current_cache_func;
+ current_cache_func = 2;
return lstat_cache(len, name,
FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
(FL_SYMLINK|FL_NOENT);
@@ -239,6 +260,8 @@ int has_symlink_or_noent_leading_path(int len, const char *name)
*/
int has_dirs_only_path(int len, const char *name, int prefix_len)
{
+ last_cache_func = current_cache_func;
+ current_cache_func = 3;
return lstat_cache(len, name,
FL_DIR|FL_FULLPATH, prefix_len) &
FL_DIR;
--
1.6.1.349.g99fa5
^ permalink raw reply related
* [PATCH/RFC v2 4/7] write_entry(): use fstat() instead of lstat() when file is open
From: Kjetil Barvik @ 2009-02-01 20:23 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kjetil Barvik
In-Reply-To: <cover.1233499703.git.barvik@broadpark.no>
Currently inside write_entry() we do an lstat(path, &st) call on a
file which have just been opened inside the exact same function. It
should be better to call fstat(fd, &st) on the file while it is open,
and it should be at least as fast as the lstat() method.
Signed-off-by: Kjetil Barvik <barvik@broadpark.no>
---
entry.c | 12 +++++++++---
1 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/entry.c b/entry.c
index 0f1f3f0..35054a5 100644
--- a/entry.c
+++ b/entry.c
@@ -94,11 +94,12 @@ static void *read_blob_entry(struct cache_entry *ce, unsigned long *size)
static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
{
unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
- int fd, ret;
+ int fd, ret, fstat_done = 0;
char *new;
struct strbuf buf = STRBUF_INIT;
unsigned long size;
size_t wrote, newsize = 0;
+ struct stat st;
switch (ce_mode_s_ifmt) {
case S_IFREG:
@@ -145,6 +146,11 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
}
wrote = write_in_full(fd, new, size);
+ /* use fstat() only when path == ce->name */
+ if (state->refresh_cache && !to_tempfile && !state->base_dir_len) {
+ fstat(fd, &st);
+ fstat_done = 1;
+ }
close(fd);
free(new);
if (wrote != size)
@@ -161,8 +167,8 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
}
if (state->refresh_cache) {
- struct stat st;
- lstat(ce->name, &st);
+ if (!fstat_done)
+ lstat(ce->name, &st);
fill_stat_cache_info(ce, &st);
}
return 0;
--
1.6.1.349.g99fa5
^ permalink raw reply related
* [PATCH/RFC v2 5/7] combine-diff.c: remove a call to fstat() inside show_patch_diff()
From: Kjetil Barvik @ 2009-02-01 20:23 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kjetil Barvik
In-Reply-To: <cover.1233499703.git.barvik@broadpark.no>
Currently inside show_patch_diff() we have an fstat() call after an
ok lstat() call. Since before the call to fstat() we have already
tested for the link case with S_ISLNK(), the fstat() can be removed.
Signed-off-by: Kjetil Barvik <barvik@broadpark.no>
---
combine-diff.c | 4 +---
1 files changed, 1 insertions(+), 3 deletions(-)
diff --git a/combine-diff.c b/combine-diff.c
index bccc018..4300319 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -713,9 +713,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
result_size = buf.len;
result = strbuf_detach(&buf, NULL);
elem->mode = canon_mode(st.st_mode);
- }
- else if (0 <= (fd = open(elem->path, O_RDONLY)) &&
- !fstat(fd, &st)) {
+ } else if (0 <= (fd = open(elem->path, O_RDONLY))) {
size_t len = xsize_t(st.st_size);
ssize_t done;
int is_file, i;
--
1.6.1.349.g99fa5
^ permalink raw reply related
* [PATCH/RFC v2 3/7] cleanup of write_entry() in entry.c
From: Kjetil Barvik @ 2009-02-01 20:23 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kjetil Barvik
In-Reply-To: <cover.1233499703.git.barvik@broadpark.no>
The switch-cases for S_IFREG and S_IFLNK was so similar that it will
be better to do some cleanup and use the common parts of it.
And the entry.c file should now be clean for 'gcc -Wextra' warnings.
Signed-off-by: Kjetil Barvik <barvik@broadpark.no>
---
entry.c | 75 +++++++++++++++++++++++++-------------------------------------
1 files changed, 30 insertions(+), 45 deletions(-)
diff --git a/entry.c b/entry.c
index 1879b83..0f1f3f0 100644
--- a/entry.c
+++ b/entry.c
@@ -78,7 +78,7 @@ static int create_file(const char *path, unsigned int mode)
return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
}
-static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size)
+static void *read_blob_entry(struct cache_entry *ce, unsigned long *size)
{
enum object_type type;
void *new = read_sha1_file(ce->sha1, &type, size);
@@ -93,36 +93,51 @@ static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned
static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
{
- int fd;
- long wrote;
-
- switch (ce->ce_mode & S_IFMT) {
- char *new;
- struct strbuf buf;
- unsigned long size;
-
+ unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
+ int fd, ret;
+ char *new;
+ struct strbuf buf = STRBUF_INIT;
+ unsigned long size;
+ size_t wrote, newsize = 0;
+
+ switch (ce_mode_s_ifmt) {
case S_IFREG:
- new = read_blob_entry(ce, path, &size);
+ case S_IFLNK:
+ new = read_blob_entry(ce, &size);
if (!new)
return error("git checkout-index: unable to read sha1 file of %s (%s)",
path, sha1_to_hex(ce->sha1));
+ if (ce_mode_s_ifmt == S_IFLNK && has_symlinks && !to_tempfile) {
+ ret = symlink(new, path);
+ free(new);
+ if (ret)
+ return error("git checkout-index: unable to create symlink %s (%s)",
+ path, strerror(errno));
+ break;
+ }
+
/*
* Convert from git internal format to working tree format
*/
- strbuf_init(&buf, 0);
- if (convert_to_working_tree(ce->name, new, size, &buf)) {
- size_t newsize = 0;
+ if (ce_mode_s_ifmt == S_IFREG &&
+ convert_to_working_tree(ce->name, new, size, &buf)) {
free(new);
new = strbuf_detach(&buf, &newsize);
size = newsize;
}
if (to_tempfile) {
- strcpy(path, ".merge_file_XXXXXX");
+ if (ce_mode_s_ifmt == S_IFREG)
+ strcpy(path, ".merge_file_XXXXXX");
+ else
+ strcpy(path, ".merge_link_XXXXXX");
fd = mkstemp(path);
- } else
+ } else if (ce_mode_s_ifmt == S_IFREG) {
fd = create_file(path, ce->ce_mode);
+ } else {
+ fd = create_file(path, 0666);
+ }
if (fd < 0) {
free(new);
return error("git checkout-index: unable to create file %s (%s)",
@@ -135,36 +150,6 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
if (wrote != size)
return error("git checkout-index: unable to write file %s", path);
break;
- case S_IFLNK:
- new = read_blob_entry(ce, path, &size);
- if (!new)
- return error("git checkout-index: unable to read sha1 file of %s (%s)",
- path, sha1_to_hex(ce->sha1));
- if (to_tempfile || !has_symlinks) {
- if (to_tempfile) {
- strcpy(path, ".merge_link_XXXXXX");
- fd = mkstemp(path);
- } else
- fd = create_file(path, 0666);
- if (fd < 0) {
- free(new);
- return error("git checkout-index: unable to create "
- "file %s (%s)", path, strerror(errno));
- }
- wrote = write_in_full(fd, new, size);
- close(fd);
- free(new);
- if (wrote != size)
- return error("git checkout-index: unable to write file %s",
- path);
- } else {
- wrote = symlink(new, path);
- free(new);
- if (wrote)
- return error("git checkout-index: unable to create "
- "symlink %s (%s)", path, strerror(errno));
- }
- break;
case S_IFGITLINK:
if (to_tempfile)
return error("git checkout-index: cannot create temporary subproject %s", path);
--
1.6.1.349.g99fa5
^ permalink raw reply related
* [PATCH/RFC v2 2/7] remove some memcpy() and strchr() calls inside create_directories()
From: Kjetil Barvik @ 2009-02-01 20:23 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kjetil Barvik
In-Reply-To: <cover.1233499703.git.barvik@broadpark.no>
Remove the call to memcpy() and strchr() for each path component
tested, and instead add each path component as we go forward inside
the while-loop.
Impact: small optimisation
Signed-off-by: Kjetil Barvik <barvik@broadpark.no>
---
* Kjetil Barvik <barvik@broadpark.no> writes:
| OK, maybe I instead should have tried to merge the function
| create_directories() with the safe_create_leading_directories() and
| *_const() functions? What do pepople think?
* Junio C Hamano <gitster@pobox.com> writes
| Strictly speaking, the safe_create_leading_* functions are meant to
| work on paths inside $GIT_DIR and are not meant for paths inside the
| work tree, which is this function is about. Their semantics are
| different with respect to adjust_shared_perm().
Ok, I think I keep the create_directories() function as is. Also
because I think a shared "create_directories()" function could maybe
make the calls to the lstat_cache() function less likely to be
sorted alphabetically, and therefore be less cache effective.
| I see existing (mis)uses of the safe_create_leading_* in
| builtin-apply.c, builtin-clone.c (the one that creates the
| work_tree, the other one is Ok), merge-recursive.c (both call sites)
| that are used to touch the work tree, but all places that create
| regular files in the work tree do not run adjust_shared_perm() but
| simply honor the user's umask.
Inside update_file_flags() in merge-recursive.c I see a call to
make_room_for_path() (if update_wd is non-zero) which calls
safe_create_leading_directories_const().
If we get lots of calls to make_room_for_path() in some cases, maybe
we will also save some stat() calls if we fix the call to
safe_create_*() at this place?
Remember that safe_create_*() calls stat() on each path component to
make sure that the component exists, same as create_directories()
used to do before.
entry.c | 23 ++++++++++++++---------
1 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/entry.c b/entry.c
index 05aa58d..1879b83 100644
--- a/entry.c
+++ b/entry.c
@@ -2,15 +2,19 @@
#include "blob.h"
#include "dir.h"
-static void create_directories(const char *path, const struct checkout *state)
+static void create_directories(const char *path, int path_len,
+ const struct checkout *state)
{
- int len = strlen(path);
- char *buf = xmalloc(len + 1);
- const char *slash = path;
-
- while ((slash = strchr(slash+1, '/')) != NULL) {
- len = slash - path;
- memcpy(buf, path, len);
+ char *buf = xmalloc(path_len + 1);
+ int len = 0;
+
+ while (len < path_len) {
+ do {
+ buf[len] = path[len];
+ len++;
+ } while (len < path_len && path[len] != '/');
+ if (len >= path_len)
+ break;
buf[len] = 0;
/*
@@ -190,6 +194,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
memcpy(path, state->base_dir, len);
strcpy(path + len, ce->name);
+ len += ce_namelen(ce);
if (!lstat(path, &st)) {
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
@@ -218,6 +223,6 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
return error("unable to unlink old '%s' (%s)", path, strerror(errno));
} else if (state->not_new)
return 0;
- create_directories(path, state);
+ create_directories(path, len, state);
return write_entry(ce, path, state, 0);
}
--
1.6.1.349.g99fa5
^ permalink raw reply related
* [PATCH/RFC v2 1/7] symlinks.c: small cleanup and optimisation
From: Kjetil Barvik @ 2009-02-01 20:23 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kjetil Barvik
In-Reply-To: <cover.1233499703.git.barvik@broadpark.no>
Simplify the if-else test in longest_match_lstat_cache() such that we
only have one simple if test. Instead of testing for 'i == cache.len'
or 'i == len', we transform this to a common test for 'i == max_len'.
And to further optimise we use 'i >= max_len' instead of 'i ==
max_len', the reason is that it is now the exact opposite of one part
inside the while-loop termination expression 'i < max_len && name[i]
== cache.path[i]', and then the compiler can probably reuse a test
instruction from it.
We also throw away the arguments to reset_lstat_cache(), such that all
the safeguard logic inside lstat_cache() is handled at one place.
Signed-off-by: Kjetil Barvik <barvik@broadpark.no>
---
symlinks.c | 44 ++++++++++++++++++++++++--------------------
1 files changed, 24 insertions(+), 20 deletions(-)
diff --git a/symlinks.c b/symlinks.c
index f262b7c..ae57e56 100644
--- a/symlinks.c
+++ b/symlinks.c
@@ -25,27 +25,30 @@ static inline int longest_match_lstat_cache(int len, const char *name,
}
i++;
}
- /* Is the cached path string a substring of 'name'? */
- if (i == cache.len && cache.len < len && name[cache.len] == '/') {
- match_len_prev = match_len;
- match_len = cache.len;
- /* Is 'name' a substring of the cached path string? */
- } else if ((i == len && len < cache.len && cache.path[len] == '/') ||
- (i == len && len == cache.len)) {
+ /*
+ * Is the cached path string a substring of 'name', is 'name'
+ * a substring of the cached path string, or is 'name' and the
+ * cached path string the exact same string?
+ */
+ if (i >= max_len && ((len > cache.len && name[cache.len] == '/') ||
+ (len < cache.len && cache.path[len] == '/') ||
+ (len == cache.len))) {
match_len_prev = match_len;
- match_len = len;
+ match_len = i;
}
*previous_slash = match_len_prev;
return match_len;
}
-static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func)
+static inline void reset_lstat_cache(void)
{
cache.path[0] = '\0';
cache.len = 0;
cache.flags = 0;
- cache.track_flags = track_flags;
- cache.prefix_len_stat_func = prefix_len_stat_func;
+ /*
+ * The track_flags and prefix_len_stat_func members is only
+ * set by the safeguard rule inside lstat_cache()
+ */
}
#define FL_DIR (1 << 0)
@@ -77,11 +80,13 @@ static int lstat_cache(int len, const char *name,
if (cache.track_flags != track_flags ||
cache.prefix_len_stat_func != prefix_len_stat_func) {
/*
- * As a safeguard we clear the cache if the values of
- * track_flags and/or prefix_len_stat_func does not
- * match with the last supplied values.
+ * As a safeguard rule we clear the cache if the
+ * values of track_flags and/or prefix_len_stat_func
+ * does not match with the last supplied values.
*/
- reset_lstat_cache(track_flags, prefix_len_stat_func);
+ reset_lstat_cache();
+ cache.track_flags = track_flags;
+ cache.prefix_len_stat_func = prefix_len_stat_func;
match_len = last_slash = 0;
} else {
/*
@@ -153,7 +158,7 @@ static int lstat_cache(int len, const char *name,
cache.path[last_slash] = '\0';
cache.len = last_slash;
cache.flags = save_flags;
- } else if (track_flags & FL_DIR &&
+ } else if ((track_flags & FL_DIR) &&
last_slash_dir > 0 && last_slash_dir <= PATH_MAX) {
/*
* We have a separate test for the directory case,
@@ -170,7 +175,7 @@ static int lstat_cache(int len, const char *name,
cache.len = last_slash_dir;
cache.flags = FL_DIR;
} else {
- reset_lstat_cache(track_flags, prefix_len_stat_func);
+ reset_lstat_cache();
}
return ret_flags;
}
@@ -190,8 +195,7 @@ void invalidate_lstat_cache(int len, const char *name)
cache.len = previous_slash;
cache.flags = FL_DIR;
} else
- reset_lstat_cache(cache.track_flags,
- cache.prefix_len_stat_func);
+ reset_lstat_cache();
}
}
@@ -200,7 +204,7 @@ void invalidate_lstat_cache(int len, const char *name)
*/
void clear_lstat_cache(void)
{
- reset_lstat_cache(0, 0);
+ reset_lstat_cache();
}
#define USE_ONLY_LSTAT 0
--
1.6.1.349.g99fa5
^ permalink raw reply related
* [PATCH/RFC v2 0/7] git checkout: more cleanups, optimisation, less lstat() calls
From: Kjetil Barvik @ 2009-02-01 20:23 UTC (permalink / raw)
To: git; +Cc: Junio C Hamano, Kjetil Barvik
Changes since v1
--- patch 1/7 ---
Kept the "match_len >= cache.len" trick only inside the
longest_match_lstat_cache() function. Also removed the arguments to
reset_lstat_cache()
--- patch 2/7 ---
Applied all comments from Junio
--- patch 3/7 ---
Kept long format strings in printf() calls, even if source lines get
longer than 80 chars. The patch touch less source lines because of
this.
--- patch 4/7 ---
Added sanity check to the if-test under which fstat() is called.
--- patch 5/7 ---
Updated commit log message
--- patch 7/7 ---
New patch which reduces the number of rmdir() calls significantly.
When tested with 'git chekcout -q my-v2.6.27' (checkout from Linux
v2.6.25 to v2.6.27), the numbers look like this before this patch:
rmdir 4532 3.314% OK: 174 NOT: 4358 0.263268 sec 58 usec/call
strings 4532 tot 354 uniq 12.802 /uniq 0.263268 sec 58 usec/call
errors 4358 tot 347 uniq 12.559 /uniq 0.249116 sec 57 usec/call
233 5.141% OK: 1 NOT: 232 "include/asm-powerpc"
187 4.126% OK: 1 NOT: 186 "include/asm-sh"
172 3.795% OK: 0 NOT: 172 "include/asm-arm"
169 3.729% OK: 1 NOT: 168 "include/asm-sparc64"
161 3.553% OK: 1 NOT: 160 "include/asm-sparc"
144 3.177% OK: 1 NOT: 143 "include/asm-ia64"
143 3.155% OK: 1 NOT: 142 "include/asm-m68knommu"
133 2.935% OK: 1 NOT: 132 "include/asm-alpha"
126 2.780% OK: 1 NOT: 125 "include/asm-s390"
122 2.692% OK: 1 NOT: 121 "include/asm-v850"
Notice the nice change in the top 10 list after this patch:
rmdir 331 0.255% OK: 174 NOT: 157 0.033435 sec 101 usec/call
strings 331 tot 331 uniq 1.000 /uniq 0.033435 sec 101 usec/call
errors 157 tot 157 uniq 1.000 /uniq 0.016300 sec 104 usec/call
1 0.302% OK: 0 NOT: 1 "Documentation"
1 0.302% OK: 0 NOT: 1 "Documentation/filesystems/configfs"
1 0.302% OK: 0 NOT: 1 "Documentation/firmware_class"
1 0.302% OK: 1 NOT: 0 "Documentation/hrtimer"
1 0.302% OK: 1 NOT: 0 "Documentation/hrtimers"
1 0.302% OK: 0 NOT: 1 "Documentation/i2c/busses"
1 0.302% OK: 1 NOT: 0 "Documentation/i386"
1 0.302% OK: 0 NOT: 1 "Documentation/networking"
1 0.302% OK: 0 NOT: 1 "Documentation/power"
1 0.302% OK: 0 NOT: 1 "Documentation/powerpc"
Kjetil Barvik (7):
symlinks.c: small cleanup and optimisation
remove some memcpy() and strchr() calls inside create_directories()
cleanup of write_entry() in entry.c
write_entry(): use fstat() instead of lstat() when file is open
combine-diff.c: remove a call to fstat() inside show_patch_diff()
lstat_cache(): print a warning if doing ping-pong between cache types
unpack-trees.c: introduce schedule_dir_for_removal()
combine-diff.c | 4 +--
entry.c | 106 +++++++++++++++++++++++++++-----------------------------
symlinks.c | 67 +++++++++++++++++++++++++----------
unpack-trees.c | 97 ++++++++++++++++++++++++++++++++++++++-------------
4 files changed, 172 insertions(+), 102 deletions(-)
^ permalink raw reply
* Git over HTTPS
From: Jason Wagner @ 2009-02-01 19:55 UTC (permalink / raw)
To: git
I'm trying to set up a git repository on DAV on Apache, but having some trouble pushing via HTTPS. I've set everything up according to the documentation-- http.sslVerify=false, .netrc setup, bare repository and update-server-info, etc. The goal is to have read access available by http, but require authed https to write. Here's the config I've been using:
Alias /public.git /var/git/public
<Location /public.git>
DAV on
Options +Indexes
#TEST <LimitExcept GET PROPFIND OPTIONS>
#TEST AuthType Basic
#TEST AuthName "Git repository"
#TEST AuthUserFile /var/git/public.git.httpusers
#TEST Require valid-user
#TEST SSLRequireSSL
#TEST </LimitExcept>
</Location>
I turned off the LimitExcept for testing purposes. Inside of public is "logging", a git init --bare repository. When testing, I can do the following:
git config repository.http.url http://jason@platinum/public.git/logging
git config repository.https.url https://jason@platinum/public.git/logging
git pull http
git push http
# fresh local repository
git pull https
When I do "git push https -f --all -v" after changing a single file, I get:
Pushing to https://jason@platinum/public.git/logging
Getting pack list
Fetching remote heads...
refs/
refs/heads/
refs/tags/
updating 'refs/heads/master'
from b8339306a895f5f18675116ab202b001cfbb95ce
to 00d2d209b62f6f5322e943726b90bbf75f8a940a
Then it freezes until I ctrl-c it. Sometimes the process stays around afterwards sometimes and needs a kill -9.
In the apache logs, I see the same requests coming in on both the http and https pushes. It seems to be locking on
[01/Feb/2009:14:36:05 -0500] 127.0.0.1 TLSv1 RC4-MD5 "MKCOL /public.git/logging/refs/ HTTP/1.1" 321
or
[01/Feb/2009:08:43:51 -0500] 127.0.0.1 TLSv1 RC4-MD5 "UNLOCK /public.git/logging/info/refs HTTP/1.1" -
I've done this on both git 1.5.6.6 and 1.6.2 to the same results. I'm using a fully updated Fedora 9 install on the server machine. Has anyone run into similar problems or have any suggestions?
---
Jason Wagner
^ permalink raw reply
* Re: Newbie question regarding 3way merge order.
From: Junio C Hamano @ 2009-02-01 19:22 UTC (permalink / raw)
To: Raimund Berger; +Cc: git, Sitaram Chamarty
In-Reply-To: <slrngo6eat.s1d.sitaramc@sitaramc.homelinux.net>
Sitaram Chamarty <sitaramc@gmail.com> writes:
> Reversing A and B is one thing, applying a sequence of
> merges in a different order is quite something else.
This point is true in theory but I haven't found it to cause problems in
practice. Textual conflicts between topics do happen, but it does not
happen so often in overlapping areas across more than two topics that
earlier resolutions to them cannot be reused by the rerere mechanism.
Semantic conflicts and dependencies are harder to deal with.
Suppose you have a topic to introduce feature A. You propose an
enhancement, validate what it does logically makes sense, queue it to the
testing integration branch and unleash it to the world for real world
testing.
---o-------* test integration branch
/ /
/ o feature A
/ /
o---o---o---o---o mainline
Later, you find out that there is a bug B, and your enhancement to
implement A does not work as intended when B triggers.
You could do two things.
(1) You extend feature A topic and queue the fix there.
---o-------*---* test integration branch
/ / /
/ o---o feature A + bugfix B
/ /
o---o---o---o---o mainline
(2) Because B is an independent bug, you can have its own topic to fix it
and merge ot to test integration, planning to merge it later to
mainline independently from feature A topic.
---o-------*---* test integration branch
/ / /
/ / o bugfix B
/ / /
/ o / feature A
/ / /
o---o---o---o---o mainline
Neither is satisfactory.
The former is bad because it takes hostage of a useful bugfix to the
feature. Without adding the feature A, which may or may not be what
people want to have, you cannot merge the fix to the mainline.
The latter is better because bugfix B could independently be merged to
mainline, but it risks you forgetting to merge it when you merge feature A
to the mainline. By the time you decide to merge feature A to the
mainline, the test integration branch has had both for some time, and you
didn't seen the problem bug B causes to feature A there, but if you merge
only feature A but not fix B to the mainline, suddenly you have a buggy
feature on the mainline.
This is where you need to use the tool right to get the most out of it.
You could do this in addition to (2).
(3) Because B is an independent bug, you can have its own topic to fix it
and merge it to the test integration, planning to merge it later to
mainline independently from feature A topic. But you already know
feature A depends on bugfix B to work correctly, so you merge the fix
to the feature as well in advance.
---o-------*---* test integration branch
/ / /
/ / o bugfix B
/ / / \
/ o-------* feature A
/ / /
o---o---o---o---o mainline
This way, even if you later choose to merge feature A, forgetting that you
have to have bugfix B for it to work, to the mainline, the merge will
bring in the necessary fix with it automatically.
---o-------*---* test integration branch
/ / /
/ / o bugfix B
/ / / \
/ o-------* feature A
/ / / \
o---o---o---o---o---* mainline
This technique to resolve conflicts and dependencies early between topics
to force a merge order to the mainline can be used not only to deal with
semantic conflicts but textual ones, too. If two branches textually
interact badly, you prepare a consolidated topic between the two, so that
you can merge A alone, B alone, or A+B together to the mainline.
If you end up merging A first and then want to merge B later (or the other
way around, merge B and then A), and if the second merge to the mainline
causes huge textual conflicts, you can instead merge the conslidated topic
A+B to the mainline.
^ permalink raw reply
* [ANNOUNCE] CGIT 0.8.2
From: Lars Hjemli @ 2009-02-01 18:57 UTC (permalink / raw)
To: Git Mailing List
Feature-release 0.8.2 (+ bugfix release 0.8.1.1) of cgit, a fast
webinterface for git, is now available:
Clone: git://hjemli.net/pub/git/cgit
Browse: http://hjemli.net/git/cgit
The highlights:
* The repository list can now be sorted on any column
* The shortlog can be expanded to show full commit messages
* New stats-view shows simple time/author-related statistics
* Log- and commit-view are now decorated with refnames
* Binary files are properly handled in diff- and plain-views (aka `hexdump -C`)
* Working text-selection in plain-view
* Plenty of bugfixes
Big thanks to everyone who provided patches, ideas and feedback!
Bugfixes included in 0.8.1.1
============================
Karl Chen (1):
Use mode 0644 for non-executable files
Lars Hjemli (13):
Use GIT-1.6.0.3
ui-repolist: handle empty agefiles
ui-log: try to disambiguate ref names
Makefile: allow cgit.conf to override platform-specific tweaks
ui-repolist: avoid build warning for strcasestr(3)
parsing.c: enable builds with NO_ICONV defined
tests/t0010-validate-html.sh: skip tests if 'tidy' is not available
tests/setup.sh: allow testsuite to fail properly with POSIX standard shell
ui-tree.c: do not add blank line when displaying blobs
Makefile: avoid libcurl when building git
ui-tag: escape tagnames properly
Avoid SEGFAULT on invalid requests
CGIT 0.8.1.1
Ramsay Jones (3):
Fix some warnings to allow -Werror
Fix tests to work on Ubuntu (dash)
Extra cygwin-specific changes
Todd Zullinger (3):
Fix tar.bz2 snapshot example
Makefile: add INSTALL var to set install command
Makefile: install cgit.{css,png} in CGIT_DATA_PATH
Tomas Carnecky (1):
ui-patch: whitespace changes in the patch generation code
Additional changes in 0.8.2
===========================
Benjamin Close (1):
Add support for sorting by Age in the repolist
Justin Waters (2):
Change toggle to more meaningful term
Add decorations to commit pages
Lars Hjemli (33):
ui-repolist: extract get_repo_modtime() from print_modtime()
ui-repolist: add support for sorting any column
ui-repolist: sort null values last
ui-repolist: implement lazy caching of repo->mtime
ui-log: add support for showing the full commit message
ui-log: (ab)use extra columns for commit message when showmsg=1
ui-log: use css to make full-log prettier
ui-snapshot: improve extraction of revision from snapshot name
ui-shared: exploit snapshot dwimmery in cgit_print_snapshot_links
ui-refs.c: show download links for all tags referring to commit objects
ui-shared: shorten the sha1 printed by cgit_object_link
Add a 'stats' page to each repo
ui-stats: enable path-filtered stats
ui-stats: replace 'enable-stats' setting with 'max-stats'
ui-shared: add and use cgit_stats_link()
ui-tree: add link to stats page per path
ui-shared: externalize add_hidden_formfields()
ui-stats.c: reuse cgit_add_hidden_formfields()
Untie logic for SCRIPT_NAME and PATH_INFO
shared.c: future-proof usage of git diff-structures
Use GIT-1.6.1
ui-snapshot.c: change mime-type for tar.gz and tar.bz2
tests/t0107-snapshot.sh: make testscript match updated snapshot behavior
ui-log: show name-decorations in log output
cgit.css: minor adjustment of commit decorations
tests/t0105-commit.sh: expect commit decorations
Add support for a custom header
html.c: use correct escaping in html attributes
Handle binary files in diffs
ui-tree: show hexdump of binary blobs
shared.c: avoid SEGFAULT when checking for binary buffers
Use GIT-1.6.1.1
CGIT 0.8.2
Natanael Copa (2):
Set prefix in snapshots when using dwimmery
return 404 if snapshot is not found
Onne Gorter (1):
changed objects are outputted, allowing for selections of code only
Robin Redeker (2):
ui-refs: avoid SEGFAULT on lightweight tags
Make all tags viewable
^ permalink raw reply
* Re: git monthly links: 2009-01
From: Sverre Rabbelier @ 2009-02-01 18:50 UTC (permalink / raw)
To: Felipe Contreras, Johannes Schindelin; +Cc: git list
In-Reply-To: <94a0d4530902010901nf415723m86c88cccf51848ee@mail.gmail.com>
Heya,
On Sun, Feb 1, 2009 at 18:01, Felipe Contreras
<felipe.contreras@gmail.com> wrote:
> Tracking an entire Windows system inside Git
> Avery Pennarun explains his endeavor of tracking windows with git...
> "If I get a virus, I can 'git revert' it."
> http://alumnit.ca/~apenwarr/log/?m=200901#21
Omg, this is so totally insane that I've been laughing the entire time
while reading the post, and then for a minute after that :P. Johannes,
I think we have a new contender for the UGFWIINI contest.
--
Cheers,
Sverre Rabbelier
^ permalink raw reply
* 026fa0d5ad Breaks installs with absolue $(gitexecdir) and $(template_dir) variables using older GNU makes
From: A Large Angry SCM @ 2009-02-01 18:24 UTC (permalink / raw)
To: Steffen Prohaska; +Cc: git, Junio C Hamano
In 026fa0d5ad9538ca76838070861531c037d7b9ba, "Move computation of
absolute paths from Makefile to runtime (in preparation for
RUNTIME_PREFIX)", the following change was made to the Makefile. The
problem is that $(abspath names...) is a relatively recent addition to
GNU make and when used in an older GNU make, the test always fails
resulting incorrect installation dirs for the templates and commands.
The new test is also wrong; (for *nix systems) in that it really wants
to test if the first character is a '/' but GNU make doesn't have a way
to do that directly. Instead, it tests if the first character is a /
_AND_ the path string does not include . or .. components,
The older test has problems itself but at least it allowed you to
specify absolute paths.
@@ -1407,17 +1417,17 @@ remove-dashes:
### Installation rules
-ifeq ($(firstword $(subst /, ,$(template_dir))),..)
-template_instdir = $(bindir)/$(template_dir)
-else
+ifeq ($(abspath $(template_dir)),$(template_dir))
template_instdir = $(template_dir)
+else
+template_instdir = $(prefix)/$(template_dir)
endif
export template_instdir
-ifeq ($(firstword $(subst /, ,$(gitexecdir))),..)
-gitexec_instdir = $(bindir)/$(gitexecdir)
-else
+ifeq ($(abspath $(gitexecdir)),$(gitexecdir))
gitexec_instdir = $(gitexecdir)
+else
+gitexec_instdir = $(prefix)/$(gitexecdir)
endif
gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir))
export gitexec_instdir
I see 3 ways to fix the problem:
1) go back to using the old test.
2) keep the new test but add a test that will break the build if
$(abspath names...) does not work.
3) something like the following (untested)
### Installation rules
temp = $(subst " ",x,$(template_dir))
temp = $(subst //,/,$(temp))
temp = $(addsuffix x,$(temp))
temp = $(subst /, ,$(temp))
ifeq ($(strip $(temp)),$(temp))
template_instdir = $(template_dir)
else
template_instdir = $(prefix)/$(template_dir)
endif
export template_instdir
temp = $(subst " ",x,$(gitexecdir))
temp = $(subst //,/,$(temp))
temp = $(addsuffix x,$(temp))
temp = $(subst /, ,$(temp))
ifeq ($(strip $(temp)),$(temp))
gitexec_instdir = $(gitexecdir)
else
gitexec_instdir = $(prefix)/$(gitexecdir)
endif
gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir))
export gitexec_instdir
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox