* Re: understanding index
From: Santi Béjar @ 2009-01-31 20:57 UTC (permalink / raw)
To: Nicolas Sebrecht; +Cc: git
In-Reply-To: <20090131201920.GC29748@ultras>
2009/1/31 Nicolas Sebrecht <nicolas.s-dev@laposte.net>:
>
> On Sat, Jan 31, 2009 at 01:09:49PM +0100, Santi Béjar wrote:
>
>> You omitted the help message of git status, where it says how to unstage:
>> # Changes to be committed:
>> # (use "git reset HEAD <file>..." to unstage)
>>
>> So to not commit foo at all:
>>
>> git reset HEAD foo
>
> Thanks. I didn't omitted the help message of git status.
These help messages (helpful messages?) were not in old version, but
in current git you get this indications when you execute "git status",
and it was what you put as "[...]".
$ git status
# On branch next
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: Makefile
#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
#
# modified: Makefile
#
> The "unstage"
> action is what I was looking for but I was wrongly presuming (and I've
> read git docs !) that 'HEAD' is a kind of shortcut to the last commit of
> the current branch (not to the last state of the working tree).
By default git reset only acts on the head of the branch and the index, so with:
git reset HEAD foo
what you are saying is, use the HEAD as the head of the branch (don't
change my branch), but put in the index the state of foo in HEAD, so
in brief reset the index state of foo.
>
> Working on branch master for example, we have in .git/HEAD :
> ref: refs/heads/master
> And in .git/refs/heads/master, the hash of the last commit of branch
> master.
Right.
HTH,
Santi
^ permalink raw reply
* Re: 'git clone' doesn't use alternates automatically?
From: Jakub Narebski @ 2009-01-31 21:08 UTC (permalink / raw)
To: James Pickens; +Cc: Git ML, Jeff King
In-Reply-To: <885649360901311208s4bc17ae3me2062b07b302291e@mail.gmail.com>
James Pickens <jepicken@gmail.com> writes:
> So, is there any reason 'git clone' shouldn't automatically use
> the alternates that it copied into the new repository? I might
> look into writing a patch if nobody objects.
Alternates are fragile with respect to garbage collecting in the
repository you borrow objects from.
--
Jakub Narebski
Poland
ShadeHawk on #git
^ permalink raw reply
* Re: understanding index
From: Björn Steinbrink @ 2009-01-31 21:18 UTC (permalink / raw)
To: Nicolas Sebrecht; +Cc: git
In-Reply-To: <20090131201920.GC29748@ultras>
On 2009.01.31 21:19:20 +0100, Nicolas Sebrecht wrote:
>
> On Sat, Jan 31, 2009 at 01:09:49PM +0100, Santi Béjar wrote:
>
> > You omitted the help message of git status, where it says how to unstage:
> > # Changes to be committed:
> > # (use "git reset HEAD <file>..." to unstage)
> >
> > So to not commit foo at all:
> >
> > git reset HEAD foo
>
> Thanks. I didn't omitted the help message of git status. The "unstage"
> action is what I was looking for but I was wrongly presuming (and I've
> read git docs !) that 'HEAD' is a kind of shortcut to the last commit of
> the current branch (not to the last state of the working tree).
>
> Working on branch master for example, we have in .git/HEAD :
> ref: refs/heads/master
> And in .git/refs/heads/master, the hash of the last commit of branch
> master.
>
> I'm missing something here. But what ?
You're missing the fact that you want to reset the index entry to the
state from HEAD. The working tree state is the modified one, and you get
that into the index using "git add". But you want the index entry to be
back at the state from HEAD. So HEAD is what you need to pass to reset
(well, actually, you can skip "HEAD" there, it's the default).
Björn
^ permalink raw reply
* Re: 'git clone' doesn't use alternates automatically?
From: James Pickens @ 2009-01-31 21:43 UTC (permalink / raw)
To: Git ML; +Cc: Jeff King, Jakub Narebski
In-Reply-To: <m3tz7fw5ek.fsf@localhost.localdomain>
On Sat, Jan 31, 2009 at 2:08 PM, Jakub Narebski <jnareb@gmail.com> wrote:
> James Pickens <jepicken@gmail.com> writes:
>
>> So, is there any reason 'git clone' shouldn't automatically use
>> the alternates that it copied into the new repository? I might
>> look into writing a patch if nobody objects.
>
> Alternates are fragile with respect to garbage collecting in the
> repository you borrow objects from.
I think that's irrelevant in this case. The scenario is that I
clone repo A, which is borrowing objects from repo B. So repo A
was already assuming that it's safe to borrow from B.
The current behavior is that the clone of A also borrows from B
automatically. What I am asking is whether 'git clone' should
take advantage of that to avoid copying redundant objects from A
into the clone. They will get deleted the first time I run 'git
gc' in the clone anyways.
James
^ permalink raw reply
* Re: [PATCH 1/2] fsck: HEAD is part of refs
From: Nanako Shiraishi @ 2009-01-31 21:45 UTC (permalink / raw)
To: Junio C Hamano
Cc: Linus Torvalds, H. Peter Anvin, Ingo Molnar, Git Mailing List
In-Reply-To: <7veiylb1in.fsf_-_@gitster.siamese.dyndns.org>
Quoting Junio C Hamano <gitster@pobox.com>:
> By default we looked at all refs but not HEAD. The only thing that
> made fsck not lose sight of comments that are only reachable from a
> detached HEAD was the reflog for the HEAD.
>
> This fixes it, with a new test.
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
I think you meant to say commits, not comments.
--
Nanako Shiraishi
http://ivory.ap.teacup.com/nanako3/
^ permalink raw reply
* Re: [PATCH 1/5] pack-check.c: minor formatting fix to match coding style
From: Nanako Shiraishi @ 2009-01-31 21:45 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <1233313517-24208-2-git-send-email-gitster@pobox.com>
Quoting Junio C Hamano <gitster@pobox.com>:
> Adjust misaligned columns and multi-line comments that violate our coding
> style before touching this file.
>
> Also fix an obvious typo.
What typo did you fix?
--
Nanako Shiraishi
http://ivory.ap.teacup.com/nanako3/
^ permalink raw reply
* Re: Newbie question regarding 3way merge order.
From: Nanako Shiraishi @ 2009-01-31 21:45 UTC (permalink / raw)
To: Raimund Berger; +Cc: git
In-Reply-To: <87myd7bo8f.fsf@gigli.quasi.internal>
Quoting "Raimund Berger" <raimund.berger@gmail.com> writes:
> I'm myself, especially since a conflicting rebase leaves the index in an
> "unmerged" state. Much like a regular merge does. It's still all
> assumptions though, or maybe I'm missing documentation .... (?)
The rebase command you run with neither -m nor -i option replays
your work on top of the upstream by running git-am with the --3way
option.
This commit introduced the feature.
commit 7f59dbbb8f8d479c1d31453eac06ec765436a780
Author: Junio C Hamano <junkio@cox.net>
Date: Mon Nov 14 00:41:53 2005 -0800
Rewrite rebase to use git-format-patch piped to git-am.
The current rebase implementation finds commits in our tree but
not in the upstream tree using git-cherry, and tries to apply
them using git-cherry-pick (i.e. always use 3-way) one by one.
Which is fine, but when some of the changes do not apply
cleanly, it punts, and punts badly.
[omitting the rest]
The message talks about what was wrong with the original, what
benefit it gives the users, and how to use it, but it doesn't
discuss how the magic works in detail.
Junio much later describes how it works, taking a real-world example
in this message:
http://thread.gmane.org/gmane.comp.version-control.git/46569/focus=46609
In short, it works by applying your changes as patches but when a
patch doesn't apply it falls back to a simplified three-way merge.
http://thread.gmane.org/gmane.comp.version-control.git/100579/focus=100602
I think Junio misremembered the history in his last message in the thread.
He says that rebase was originally a format-patch piped to am, but
before the commit 7f59dbbb8f8d479c1d31453eac06ec765436a780 it was
done by a series of cherry-pick.
--
Nanako Shiraishi
http://ivory.ap.teacup.com/nanako3/
^ permalink raw reply
* [PATCH 2/4] Add find_insert_index, insert_at_index and clear_func functions to string_list
From: Marius Storm-Olsen @ 2009-01-31 21:47 UTC (permalink / raw)
To: git; +Cc: Marius Storm-Olsen
In-Reply-To: <cover.1233438174.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 1/4] Add log.mailmap as configurational option for mailmap location
From: Marius Storm-Olsen @ 2009-01-31 21:47 UTC (permalink / raw)
To: git; +Cc: Marius Storm-Olsen
In-Reply-To: <cover.1233438174.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 0/4] Extend mailmap functionality
From: Marius Storm-Olsen @ 2009-01-31 21:47 UTC (permalink / raw)
To: git; +Cc: Marius Storm-Olsen
So, 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 patch serie for 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
* [PATCH 3/4] Add map_user() and clear_mailmap() to mailmap
From: Marius Storm-Olsen @ 2009-01-31 21:48 UTC (permalink / raw)
To: git; +Cc: Marius Storm-Olsen
In-Reply-To: <cover.1233438174.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 | 65 ++++++++++---
mailmap.c | 198 ++++++++++++++++++++++++++++++++++++----
mailmap.h | 4 +
3 files changed, 234 insertions(+), 33 deletions(-)
diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index cacbeea..4226683 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -50,20 +50,36 @@ 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 allow mailmap to replace both the name and the email of a commit
+matching the specified commit email address.
+
++
+--
+ Proper Name <proper@email.xx> Commit Name <commit@email.xx>
+--
++
+Which allow 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 +102,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 4/4] Change current mailmap usage to do matching on both name and email of author/committer.
From: Marius Storm-Olsen @ 2009-01-31 21:48 UTC (permalink / raw)
To: git; +Cc: Marius Storm-Olsen
In-Reply-To: <cover.1233438174.git.marius@trolltech.com>
Signed-off-by: Marius Storm-Olsen <marius@trolltech.com>
---
Documentation/git-shortlog.txt | 13 +----
Documentation/pretty-formats.txt | 2 +
builtin-blame.c | 50 ++++++++++++-------
builtin-shortlog.c | 22 +++++++--
pretty.c | 59 ++++++++++++-----------
t/t4203-mailmap.sh | 99 ++++++++++++++++++++++++++++++++++++++
6 files changed, 183 insertions(+), 62 deletions(-)
diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index 4226683..1cdb03f 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -58,27 +58,20 @@ 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 allow mailmap to replace both the name and the email of a commit
-matching the specified commit email address.
-
-+
+which allow 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 allow mailmap to replace both the name and the email of a commit
+which allow 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
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
* Re: 'git clone' doesn't use alternates automatically?
From: Jeff King @ 2009-01-31 21:55 UTC (permalink / raw)
To: James Pickens; +Cc: Git ML
In-Reply-To: <885649360901311208s4bc17ae3me2062b07b302291e@mail.gmail.com>
On Sat, Jan 31, 2009 at 01:08:16PM -0700, James Pickens wrote:
> Well, the clone gets the alternates either way. It just doesn't
> use them to avoid copying the data unless I give -s. More
The other key change is that you don't depend on the origin in your
alternates when you don't use "-s".
> So, is there any reason 'git clone' shouldn't automatically use
> the alternates that it copied into the new repository? I might
> look into writing a patch if nobody objects.
I think the reason "-s" isn't the default is that alternates are fragile
(as Jakub mentioned), and we don't want ot set them up without the user
asking to do so.
So from what you've posted (but I haven't double checked or looked at
the code), it sounds like the current behavior is:
- with "-s", add the origin as an alternate, and use alternates while
cloning
- "with --reference", add some other repo as an alternate, and use
alternates while cloning
- without either, copy alternates from origin, but _don't_ use
alternates while cloning
The last one seems a little silly. Why bother setting up the alternates
if you're not going to use them? I guess because we might not be able to
get the objects at all, otherwise, and we need to know where to copy
them from. But either:
- that is an implementation-specific detail of clone, and those
alternates should go away after we clone
or
- we should fully respect those alternates
The only downside to the latter is that now somebody who has cloned a
repository with alternates now has an alternates-based repository and
might not know it (i.e., they might have been the one who set up
alternates in the origin).
-Peff
^ permalink raw reply
* Re: [PATCH 1/5] pack-check.c: minor formatting fix to match coding style
From: Marius Storm-Olsen @ 2009-01-31 22:00 UTC (permalink / raw)
To: Nanako Shiraishi; +Cc: Junio C Hamano, git
In-Reply-To: <20090201064528.6117@nanako3.lavabit.com>
Nanako Shiraishi said the following on 31.01.2009 22:45:
> Quoting Junio C Hamano <gitster@pobox.com>:
>
>> Adjust misaligned columns and multi-line comments that violate our coding
>> style before touching this file.
>>
>> Also fix an obvious typo.
>
> What typo did you fix?
- err = error("%s SHA1 does not match its inddex",
+ err = error("%s SHA1 does not match its index",
--
.marius
^ permalink raw reply
* Re: understanding index
From: Nicolas Sebrecht @ 2009-01-31 22:07 UTC (permalink / raw)
To: git
In-Reply-To: <adf1fd3d0901311257j1501d222nc24514b013bd13f3@mail.gmail.com>
On Sat, Jan 31, 2009 at 09:57:29PM +0100, Santi Béjar wrote:
> By default git reset only acts on the head of the branch and the index, so with:
>
> git reset HEAD foo
>
> what you are saying is, use the HEAD as the head of the branch (don't
> change my branch), but put in the index the state of foo in HEAD, so
> in brief reset the index state of foo.
Thank you all for the explanations.
--
Nicolas Sebrecht
^ permalink raw reply
* Re: diff settings
From: Keith Cascio @ 2009-01-31 23:10 UTC (permalink / raw)
To: Ted Pavlic; +Cc: git
In-Reply-To: <4984AACD.20600@tedpavlic.com>
On Sat, 31 Jan 2009, Ted Pavlic wrote:
> As I already discussed, Mercurial has both "diff.primer" and "primer.diff"
> precisely because "diff" settings affects so many Hg commands.
Yes I remember you did mention that.
> In particular, in my .hgrc, I have:
> [diff]
> git = 1
> which causes all Mercurial commands that need to generate a diff to use
> gitdiff rather than truediff. However, if I *wanted* to apply a set of flags
> to a particular command, I could (using the equivalent "git" commands)...
> [defaults]
> pull = --rebase
> commit = -a
> format-patch = -M
> So I see a purpose for both *.defaults and defaults.*. Of course, aliases are
> also nice (IIRC, Mercurial doesn't have "aliases", but I could be wrong).
> Mercurial users are very happy with having both available. I imagine git
> people would be too.
I like it! I'm all for it. Perfect opportunity for future work. In fact, I
think I already glanced at the part of the code that's the right place to make
that stuff happen.
-- Keith
^ permalink raw reply
* Re: [PATCH 1/2] fsck: HEAD is part of refs
From: Junio C Hamano @ 2009-02-01 0:53 UTC (permalink / raw)
To: Johannes Sixt
Cc: Linus Torvalds, H. Peter Anvin, Ingo Molnar, Git Mailing List
In-Reply-To: <4982C7FA.20107@viscovery.net>
Johannes Sixt <j.sixt@viscovery.net> writes:
> Junio C Hamano schrieb:
>> By default we looked at all refs but not HEAD. The only thing that
>> made fsck not lose sight of comments that are only reachable from a
>> detached HEAD was the reflog for the HEAD.
>>
>> This fixes it, with a new test.
> [...]
>> +test_expect_success setup '
>> + test_commit A &&
>> + git checkout HEAD^0 &&
>> + test_commit B &&
>> + git reflog expire --expire=now --all
>> +'
>> +
>> +test_expect_success 'HEAD is part of refs' '
>> + test 0 = $(git fsck | wc -l)
>
> I'm scratching my head over this. This test succeeds even without the
> change... (Tested with a34a9db.)
You are right. I forgot that test_commit creates an extra tag. By adding
git tag -d B &&
after "test_commit B &&", you would expose the issue in the existing code.
^ permalink raw reply
* Re: [PATCH 1/5] pack-check.c: minor formatting fix to match coding style
From: Nanako Shiraishi @ 2009-02-01 0:52 UTC (permalink / raw)
To: Marius Storm-Olsen; +Cc: Junio C Hamano, git
In-Reply-To: <4984CA0C.7010703@trolltech.com>
Quoting Marius Storm-Olsen <marius@trolltech.com>:
>Nanako Shiraishi said the following on 31.01.2009 22:45:
>> What typo did you fix?
>
>- err = error("%s SHA1 does not match its inddex",
>+ err = error("%s SHA1 does not match its index",
Ah, you are right. Sorry for the noise.
--
Nanako Shiraishi
http://ivory.ap.teacup.com/nanako3/
^ permalink raw reply
* Re: 'git clone' doesn't use alternates automatically?
From: Junio C Hamano @ 2009-02-01 0:55 UTC (permalink / raw)
To: James Pickens; +Cc: Git ML, Jeff King
In-Reply-To: <885649360901311208s4bc17ae3me2062b07b302291e@mail.gmail.com>
James Pickens <jepicken@gmail.com> writes:
> So, is there any reason 'git clone' shouldn't automatically use
> the alternates that it copied into the new repository?
When you say "git clone" without -s, you are saying "I do not want to use
the repository I am cloning from as my alternate, because I do not know if
will stay stable. I do not trust it."
This would be a very sensible way to clone, if you were cloning my
repository whose 'pu' and its constituent topic branches are subject to
rewinding at any time. After I rebase some of the branches and rebuild
'pu', and prune the unnecessary objects from my repository, the objects
you may have been borrowing from me will be gone from my repository. Of
course, I can remove my repository altogether any time, and when that
happens, your repository will have many missing objects.
That is why "-s" is not the default.
Only when you positively know that the other repository will not drop
branches or rewind them, perhaps because you control that repository
yourself, it is safe to use it as your alternate, and you use commands
like "git clone -s" and/or "git clone --reference" to do so.
Side note. People on k.org are encouraged to use Linus's
repository as an alternate to save space on the k.org machine,
because it is known that Linus's repository will never rewind its
branches.
Now, if you are cloning from a local filesystem, by default we will copy
the objects/info/alternates from the source repository to the new one. It
may be debatable if this is a sensible thing to do. On one hand, because
you are saying you don't trust if the objects in the source repository
will stay stable by not giving "-s", it might be sensible not to trust its
choice of alternates either. But in such a case, you can always use file://
URL when cloning to get a full freestanding copy.
I suspect you are trying to improve the other extreme end: trusting all
the other repositories involved in the cloning process a lot more than the
code currently does.
I do not think it is a bad thing to do per-se.
I haven't looked at the codepaths involved recently, but if I recall
correctly, optimizing of cloning from a repository that uses alternates
itself was never a part of the initial design considerations. I suspect
there may be an ample room for you to optimize things.
^ permalink raw reply
* Re: [PATCH] http-push: refactor request url creation
From: Junio C Hamano @ 2009-02-01 0:55 UTC (permalink / raw)
To: Tay Ray Chuan; +Cc: git, Johannes Schindelin
In-Reply-To: <4983929B.2010901@gmail.com>
Tay Ray Chuan <rctay89@gmail.com> writes:
> Currently, functions that deal with objects on the remote repository
> have to allocate and do strcpys to generate the URL.
>
> This patch saves them this trouble, by providing two functions,
> "append_remote_object_url" and "get_remote_object_url".
>
> Both generate a URL, with either the object's 2-digit hex directory
> (eg. /objects/a1/), or the complete object location (eg.
> /objects/a1/b2).
>
> However, they differ in that "append_remote_object_url" appends this
> URL to a strbuf, while "get_remote_object_url" wraps around the former
> and returns the URL directly in char*. Users usually would use
> "get_remote_object_url", but may find "append_remote_object_url"
> useful if they require further string operations on the URL.
>
> Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
> Acked-by: Junio C Hamano <gitster@pobox.com>
Thanks, I'll queue this to 'pu' for now, but please stop randomly adding
"Acked-by" from other people, unless you conferred with them on the
exact version of your patch you are submitting.
If somebody said "The version that I reviewed looked sensible", it becomes
irrelevant after you changed your patch in response to people's comments,
exactly because what you are sending is different from what they reviewed.
It is up to them, not you, to see if the issues raised in their comments
are addressed in the new patch to their satisfaction.
^ permalink raw reply
* Re: [PATCH 1/2] fsck: HEAD is part of refs
From: Junio C Hamano @ 2009-02-01 0:57 UTC (permalink / raw)
To: Nanako Shiraishi
Cc: Linus Torvalds, H. Peter Anvin, Ingo Molnar, Git Mailing List
In-Reply-To: <20090201064520.6117@nanako3.lavabit.com>
Nanako Shiraishi <nanako3@lavabit.com> writes:
> Quoting Junio C Hamano <gitster@pobox.com>:
>
>> By default we looked at all refs but not HEAD. The only thing that
>> made fsck not lose sight of comments that are only reachable from a
>> detached HEAD was the reflog for the HEAD.
>>
>> This fixes it, with a new test.
>>
>> Signed-off-by: Junio C Hamano <gitster@pobox.com>
>
> I think you meant to say commits, not comments.
I'll amend when I fix the broken test J6t pointed out.
^ permalink raw reply
* Re: 'git clone' doesn't use alternates automatically?
From: Junio C Hamano @ 2009-02-01 1:19 UTC (permalink / raw)
To: Jeff King; +Cc: James Pickens, Git ML
In-Reply-To: <20090131215514.GB9415@coredump.intra.peff.net>
Jeff King <peff@peff.net> writes:
> - without either, copy alternates from origin, but _don't_ use
> alternates while cloning
Are you talking about a local clone optimization that does hardlink from
the source repository?
I am fairly certain that copying alternates from the source repository was
not an intended behaviour but was a consequence of lazy coding of how we
copy (or link) everything from it. The original was literally the simple
matter of:
find objects ! -type d -print | cpio $cpio_quiet_flag -pumd$l "$GIT_DIR/"
whose intention was to copy objects/?? and objects/pack/. and it wasn't
even part of the design consideration to worry about what would happen to
the alternates the source repository might have in objects/info/.
^ permalink raw reply
* Re: [PATCH] Switch receive.denyCurrentBranch to "refuse"
From: Junio C Hamano @ 2009-02-01 1:27 UTC (permalink / raw)
To: Nanako Shiraishi; +Cc: Johannes Schindelin, Jay Soffian, git
In-Reply-To: <20090131095622.6117@nanako3.lavabit.com>
Nanako Shiraishi <nanako3@lavabit.com> writes:
> How about doing these before the 1.7.0 release?
> ...
> What do people think?
I haven't manged to convince myself about the "git init" change (I have
the code and also I've looked at the extent of damage the change causes to
the existing test suite), but at least I think it is a sensible suggestion
to differentiate between unconfigured-wwwand-defaults-to-warn case and
configured-to-warn-so-we-warn case. Something like this.
-- >8 --
Subject: [PATCH] receive-pack: explain what to do when push updates the current branch
This makes "git push" issue a more detailed instruction when a user pushes
into the current branch of a non-bare repository without having an
explicit configuration set to receive.denycurrentbranch. In such a case,
it will also tell the user that the default will change to refusal in a
future version of git.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin-receive-pack.c | 58 +++++++++++++++++++++++++++++++++++------------
t/t5516-fetch-push.sh | 6 ++--
2 files changed, 46 insertions(+), 18 deletions(-)
diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c
index 6564a97..f2c94fc 100644
--- a/builtin-receive-pack.c
+++ b/builtin-receive-pack.c
@@ -12,6 +12,7 @@
static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
enum deny_action {
+ DENY_UNCONFIGURED,
DENY_IGNORE,
DENY_WARN,
DENY_REFUSE,
@@ -19,7 +20,7 @@ enum deny_action {
static int deny_deletes = 0;
static int deny_non_fast_forwards = 0;
-static enum deny_action deny_current_branch = DENY_WARN;
+static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
static int receive_fsck_objects;
static int receive_unpack_limit = -1;
static int transfer_unpack_limit = -1;
@@ -214,6 +215,35 @@ static int is_ref_checked_out(const char *ref)
return !strcmp(head, ref);
}
+static char *warn_unconfigured_deny_msg[] = {
+ "Updating the currently checked out branch may cause confusion,",
+ "as the index and work tree do not reflect changes that are in HEAD."
+ "As a result, you may see the changes you just pushed into it",
+ "reverted when you run 'git diff' over there, and you may want",
+ "to run 'git reset --hard' before starting to work to recover.",
+ "",
+ "You can set 'receive.denyCurrentBranch' configuration variable to",
+ "'refuse' in the repository to forbid pushing into the current branch",
+ "of it."
+ "",
+ "To allow pushing into the current branch, you can set it to 'ignore';",
+ "but this is not recommended unless you really know what you are doing.",
+ "",
+ "To squelch this message, you can set it to 'warn'.",
+ "",
+ "Note that the default will change in a future version of git",
+ "to refuse updating the currentbranch unless you have the",
+ "configuration variable set to either 'ignore' or 'warn'."
+};
+
+static void warn_unconfigured_deny(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(warn_unconfigured_deny_msg); i++)
+ warning(warn_unconfigured_deny_msg[i]);
+}
+
+
static const char *update(struct command *cmd)
{
const char *name = cmd->ref_name;
@@ -227,22 +257,20 @@ static const char *update(struct command *cmd)
return "funny refname";
}
- switch (deny_current_branch) {
- case DENY_IGNORE:
- break;
- case DENY_WARN:
- if (!is_ref_checked_out(name))
+ if (is_ref_checked_out(name)) {
+ switch (deny_current_branch) {
+ case DENY_IGNORE:
break;
- warning("updating the currently checked out branch; this may"
- " cause confusion,\n"
- "as the index and working tree do not reflect changes"
- " that are now in HEAD.");
- break;
- case DENY_REFUSE:
- if (!is_ref_checked_out(name))
+ case DENY_UNCONFIGURED:
+ case DENY_WARN:
+ warning("updating the current branch");
+ if (deny_current_branch == DENY_UNCONFIGURED)
+ warn_unconfigured_deny();
break;
- error("refusing to update checked out branch: %s", name);
- return "branch is currently checked out";
+ case DENY_REFUSE:
+ error("refusing to update checked out branch: %s", name);
+ return "branch is currently checked out";
+ }
}
if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 4426df9..89649e7 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -492,7 +492,7 @@ test_expect_success 'warn on push to HEAD of non-bare repository' '
git checkout master &&
git config receive.denyCurrentBranch warn) &&
git push testrepo master 2>stderr &&
- grep "warning.*this may cause confusion" stderr
+ grep "warning: updating the current branch" stderr
'
test_expect_success 'deny push to HEAD of non-bare repository' '
@@ -510,7 +510,7 @@ test_expect_success 'allow push to HEAD of bare repository (bare)' '
git config receive.denyCurrentBranch true &&
git config core.bare true) &&
git push testrepo master 2>stderr &&
- ! grep "warning.*this may cause confusion" stderr
+ ! grep "warning: updating the current branch" stderr
'
test_expect_success 'allow push to HEAD of non-bare repository (config)' '
@@ -520,7 +520,7 @@ test_expect_success 'allow push to HEAD of non-bare repository (config)' '
git config receive.denyCurrentBranch false
) &&
git push testrepo master 2>stderr &&
- ! grep "warning.*this may cause confusion" stderr
+ ! grep "warning: updating the current branch" stderr
'
test_expect_success 'fetch with branches' '
--
1.6.1.2.312.g5be3c
^ permalink raw reply related
* Re: What's cooking in git.git (Jan 2009, #07; Wed, 28)
From: Junio C Hamano @ 2009-02-01 1:31 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Jeff King, Sverre Rabbelier, Pieter de Bie, git
In-Reply-To: <alpine.DEB.1.00.0901301415260.3586@pacific.mpi-cbg.de>
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> How about installing
>
> [branch "master"]
> remote = origin
> merge = refs/heads/master
>
> by default? It is a safe bet that this will be the case for 99% of all
> users that want to clone an empty repository (especially if they are
> putting their public repositories on something like repo.or.cz, where you
> cannot change the default branch from "master" to something else).
I think this is a reasonable thing to do. Even though cloning from a void
is not entirely a reasonable thing to do to begin with, because we are
going ahead to allow it now, it would be the best thing to do when cloning
a repository served by the currently deployed git.
We _could_ do better if we were to resurrect my earlier series to add
"where does the HEAD point at" protocol extension, but even then we would
need a fallback like your suggestion when talking to older servers anyway.
^ permalink raw reply
* Re: Something weird is happening...
From: Junio C Hamano @ 2009-02-01 1:31 UTC (permalink / raw)
To: Sverre Rabbelier; +Cc: H. Peter Anvin, Git Mailing List, Ingo Molnar
In-Reply-To: <bd6139dc0901290545s1a5ba310u549338e823762ae1@mail.gmail.com>
Sverre Rabbelier <srabbelier@gmail.com> writes:
> On Thu, Jan 29, 2009 at 07:56, Junio C Hamano <gitster@pobox.com> wrote:
>>> Okay, what is going on here?
>
> Is this perhaps related to the problem the github guys were having,
> its sounds similar, problem not showing up until after upgrade,
> missing objects, no?
I do not think so. The push problem does not lead to any repository
corruption, and does not have anything to do with the fsck misreporting a
blob that exists in an alternate object store as missing, either.
.
^ 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