Re: commit: support commit.verbose and --no-verbose

2014-05-24 Thread Jeremiah Mahler

On Fri, May 23, 2014 at 04:22:22PM -0500, Caleb Thompson wrote:
> This patch allows people to set `commit.verbose` to implicitly send 
> `--verbose`
...
>  
> +cat >check-for-no-diff < +#!$SHELL_PATH
> +exec grep -v '^diff --git' "\$1"
> +EOF
> +chmod +x check-for-no-diff
> +

For new tests, commands like this should be placed inside a
test_expect_success structure.  However, I can see why you did it this
way since the code just above it does it this way.
Perhaps others will have some recommendations.

Also, <<\-EOF is used instead of   
> +test_expect_success 'commit shows verbose diff with set commit.verbose' '
> + echo morecontent >file &&
> + git add file &&
> + git config commit.verbose true &&
> + check_message message
> +'

'test_config' should be used to set config variables since it
also takes care of un-setting them when the test is complete.

test_expect_success 'commit shows verbose diff with set commit.verbose' '
echo morecontent >file &&
git add file &&
test_config commit.verbose true &&
check_message message
'

-- 
Jeremiah Mahler
jmmah...@gmail.com
http://github.com/jmahler
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH 1/2] remote: defer repacking packed-refs when deleting refs

2014-05-24 Thread Jens Lindström
On Fri, May 23, 2014 at 7:09 PM, Junio C Hamano  wrote:
> Jens Lindström  writes:
>> One additional change was required in
>> builtin/remote.c:remove_branches().  It used to pass in the expected
>> SHA-1 of the ref to delete_ref(), which only works if the ref exists.
>> If repack_without_refs() is called first, most refs won't exist,...
>
> Why?  A ref, before you start pruning or removing a remote, could be
> in one of these three states.
>
>  (a) only loose refs/remotes/them/frotz exists
>  (b) both loose and packed refs/remotes/them/nitfol exist
>  (c) only packed refs/remotes/them/xyzzy exists
>
> And then you repack packed-refs file without these three refs.  When
> you do so, you know that you would need to remove frotz and nitfol,
> and also you know you do not want to call delete_ref for xyzzy, no?
>
> In other words, the problem you are describing in remove_branches(),
> that it wants to make sure that the ref-to-be-removed still points
> at the expected object, does not sound like it is doing anything
> inherently wrong---rather, it sounds like you didn't make necessary
> changes to the caller to make sure that you do not call delete_ref()
> on something you know you removed already.
>
> Puzzled

There is one reason why one would want to call delete_ref() even if
the ref itself was already fully deleted by repack_without_refs()
(because it was only packed) and that is that delete_ref() also
removes the ref log, if there is one.  We could refactor the deletion
to

  1) repack_without_refs() on all refs
  2) delete_ref() on still existing (loose) refs
  3) delete_ref_log() on all refs

to let us only call delete_ref() on existing refs, and then keep the
current value check.

Personally I don't feel that we're solving an important problem here.
Making sure not to overwrite a ref that has been updated since we read
its value is of course of great value for 'receive-pack' and 'commit'
and such, but in this case we're removing a remote and all its
remote-tracking branches and configuration.  If a remote-tracking
branch is updated concurrently, the current value check would fail,
and the remote configuration and that one branch would remain.  But if
the update had happened just an instant earlier, just before we
started removing the remote, we would have happily deleted that
remote-tracking branch as well, throwing away whatever information the
update stored in it.

/ Jens
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: commit: support commit.verbose and --no-verbose

2014-05-24 Thread Duy Nguyen
On Sat, May 24, 2014 at 4:22 AM, Caleb Thompson  wrote:
> This patch allows people to set `commit.verbose` to implicitly send 
> `--verbose`
> to `git commit`. It also introduces `--no-verbose` to override the 
> configuration
> setting.

The code change looks fine to me (no opinion if we should do this though).

> --- a/Documentation/git-commit.txt
> +++ b/Documentation/git-commit.txt
> @@ -282,7 +282,14 @@ configuration variable documented in 
> linkgit:git-config[1].
> Show unified diff between the HEAD commit and what
> would be committed at the bottom of the commit message
> template.  Note that this diff output doesn't have its
> -   lines prefixed with '#'.
> +   lines prefixed with '#'.  The `commit.verbose` configuration
> +   variable can be set to true to implicitly send this option.
> +
> +-V::

No, I don't think -V is the negative form of -v if you use OPT_BOOL().

> +--no-verbose::
> +   Do not show the unified diff  at the bottom of the commit message
> +   template.  This is the default behavior, but can be used to override
> +   the`commit.verbose` configuration variable.
-- 
Duy
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] git-p4: Do not include diff in spec file when just preparing p4

2014-05-24 Thread Pete Wyckoff
frrr...@gmail.com wrote on Sat, 24 May 2014 02:39 +0100:
> The diff information render the spec file unusable as is by p4,
> do not include it when run with --prepare-p4-only so that the
> given file can be directly passed to p4.
> 
> With --prepare-p4-only, git-p4 already tells the user it can use
> p4 submit with the generated spec file. This fails because of the
> diff being present in the file. Not including the diff fixes that.
> 
> Without --prepare-p4-only, keeping the diff makes sense for a
> quick review of the patch before submitting it. And does not cause
> problems with p4 as we remove it programmatically.
> 
> Signed-off-by: Maxime Coste 

Hi Maxime.  This looks really good.  Even the Windows section
is fine; thanks for paying attention there too.

I'm not particularly worried about having a new test for this.
Your tweak to the existing 9807 is fine.  Unless of course you
have one ready to go.

Acked-by: Pete Wyckoff 

You might add my ack and send it directly to Junio + CC the list.
It'll be a nice improvement for the next available release.

-- Pete
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


-- Kedves Email felhasználói;

2014-05-24 Thread Administrator System ®



-- 
Kedves Email felhasználói;

Túllépte a határt 23.432 tárolása az e-postafiók beállítva a
WEB SERVICE / Administrator, és akkor problémái küldött
és a bejövő üzenetek, amíg meg újból érvényesíti az e-mail címét. A
szükséges eljárások
nyújtottak be az alábbi a véleménye, ellenőrizze kattintva
Az alábbi linkre és töltse ki az adatokat, hogy érvényesítse az e-mail címét.

Kérjük, kattintson ide

http://updattwwer.jigsy.com/

Növelni az e-mail kvótát az e-mail.
Figyelem!
Ennek elmulasztása azt eredményezi, hogy korlátozott hozzáférést a postafiók.
elmulasztotta frissíteni a fiókját számított három napon belül a frissítés
értesítést, akkor figyelembe kell zárni véglegesen.

Tisztelettel,
Rendszergazda ®
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH] git-p4: Do not include diff in spec file when just preparing p4

2014-05-24 Thread Maxime Coste
The diff information render the spec file unusable as is by p4,
do not include it when run with --prepare-p4-only so that the
given file can be directly passed to p4.

With --prepare-p4-only, git-p4 already tells the user it can use
p4 submit with the generated spec file. This fails because of the
diff being present in the file. Not including the diff fixes that.

Without --prepare-p4-only, keeping the diff makes sense for a
quick review of the patch before submitting it. And does not cause
problems with p4 as we remove it programmatically.

Signed-off-by: Maxime Coste 
Acked-by: Pete Wyckoff 
---
 git-p4.py| 49 +---
 t/t9807-git-p4-submit.sh |  3 ++-
 2 files changed, 28 insertions(+), 24 deletions(-)

diff --git a/git-p4.py b/git-p4.py
index 773cafc..7bb0f73 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -1238,6 +1238,28 @@ class P4Submit(Command, P4UserMap):
 if response == 'n':
 return False
 
+def get_diff_description(self, editedFiles):
+# diff
+if os.environ.has_key("P4DIFF"):
+del(os.environ["P4DIFF"])
+diff = ""
+for editedFile in editedFiles:
+diff += p4_read_pipe(['diff', '-du',
+  wildcard_encode(editedFile)])
+
+# new file diff
+newdiff = ""
+for newFile in filesToAdd:
+newdiff += " new file \n"
+newdiff += "--- /dev/null\n"
+newdiff += "+++ %s\n" % newFile
+f = open(newFile, "r")
+for line in f.readlines():
+newdiff += "+" + line
+f.close()
+
+return diff + newdiff
+
 def applyCommit(self, id):
 """Apply one commit, return True if it succeeded."""
 
@@ -1398,34 +1420,15 @@ class P4Submit(Command, P4UserMap):
 submitTemplate += " Variable git-p4.skipUserNameCheck 
hides this message.\n"
 
 separatorLine = " everything below this line is just the diff 
###\n"
+if not self.prepare_p4_only:
+submitTemplate += separatorLine
+submitTemplate += self.get_diff_description(editedFiles)
 
-# diff
-if os.environ.has_key("P4DIFF"):
-del(os.environ["P4DIFF"])
-diff = ""
-for editedFile in editedFiles:
-diff += p4_read_pipe(['diff', '-du',
-  wildcard_encode(editedFile)])
-
-# new file diff
-newdiff = ""
-for newFile in filesToAdd:
-newdiff += " new file \n"
-newdiff += "--- /dev/null\n"
-newdiff += "+++ %s\n" % newFile
-f = open(newFile, "r")
-for line in f.readlines():
-newdiff += "+" + line
-f.close()
-
-# change description file: submitTemplate, separatorLine, diff, newdiff
 (handle, fileName) = tempfile.mkstemp()
 tmpFile = os.fdopen(handle, "w+")
 if self.isWindows:
 submitTemplate = submitTemplate.replace("\n", "\r\n")
-separatorLine = separatorLine.replace("\n", "\r\n")
-newdiff = newdiff.replace("\n", "\r\n")
-tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
+tmpFile.write(submitTemplate)
 tmpFile.close()
 
 if self.prepare_p4_only:
diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh
index 4caf36e..7fab2ed 100755
--- a/t/t9807-git-p4-submit.sh
+++ b/t/t9807-git-p4-submit.sh
@@ -403,7 +403,8 @@ test_expect_success 'submit --prepare-p4-only' '
git commit -m "prep only add" &&
git p4 submit --prepare-p4-only >out &&
test_i18ngrep "prepared for submission" out &&
-   test_i18ngrep "must be deleted" out
+   test_i18ngrep "must be deleted" out &&
+   ! test_i18ngrep "everything below this line is just the diff" 
out
) &&
(
cd "$cli" &&
-- 
1.9.3

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH] gc --auto: do not lock refs in the background

2014-05-24 Thread Nguyễn Thái Ngọc Duy
9f673f9 (gc: config option for running --auto in background -
2014-02-08) puts "gc --auto" in background to reduce user's wait
time. Part of the garbage collecting is pack-refs and pruning
reflogs. These require locking some refs and may abort other processes
trying to lock the same ref. If gc --auto is fired in the middle of a
script, gc's holding locks in the background could fail the script,
which could never happen before 9f673f9.

Keep running pack-refs and "reflog --prune" in foreground to stop
parallel ref updates. The remaining background operations (repack,
prune and rerere) should impact running git processes.

Reported-by: Adam Borowski 
Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 builtin/gc.c | 26 --
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/builtin/gc.c b/builtin/gc.c
index 85f5c2b..8d219d8 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -26,6 +26,7 @@ static const char * const builtin_gc_usage[] = {
 };
 
 static int pack_refs = 1;
+static int prune_reflogs = 1;
 static int aggressive_depth = 250;
 static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
@@ -258,6 +259,19 @@ static const char *lock_repo_for_gc(int force, pid_t* 
ret_pid)
return NULL;
 }
 
+static int gc_before_repack(void)
+{
+   if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
+   return error(FAILED_RUN, pack_refs_cmd.argv[0]);
+
+   if (prune_reflogs && run_command_v_opt(reflog.argv, RUN_GIT_CMD))
+   return error(FAILED_RUN, reflog.argv[0]);
+
+   pack_refs = 0;
+   prune_reflogs = 0;
+   return 0;
+}
+
 int cmd_gc(int argc, const char **argv, const char *prefix)
 {
int aggressive = 0;
@@ -320,12 +334,15 @@ int cmd_gc(int argc, const char **argv, const char 
*prefix)
fprintf(stderr, _("Auto packing the repository 
for optimum performance.\n"));
fprintf(stderr, _("See \"git help gc\" for manual 
housekeeping.\n"));
}
-   if (detach_auto)
+   if (detach_auto) {
+   if (gc_before_repack())
+   return -1;
/*
 * failure to daemonize is ok, we'll continue
 * in foreground
 */
daemonize();
+   }
} else
add_repack_all_option();
 
@@ -337,11 +354,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
name, (uintmax_t)pid);
}
 
-   if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
-   return error(FAILED_RUN, pack_refs_cmd.argv[0]);
-
-   if (run_command_v_opt(reflog.argv, RUN_GIT_CMD))
-   return error(FAILED_RUN, reflog.argv[0]);
+   if (gc_before_repack())
+   return -1;
 
if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
return error(FAILED_RUN, repack.argv[0]);
-- 
1.9.1.346.ga2b5940

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH] gc --auto: do not lock refs in the background

2014-05-24 Thread Duy Nguyen
On Sun, May 25, 2014 at 7:38 AM, Nguyễn Thái Ngọc Duy  wrote:
> Keep running pack-refs and "reflog --prune" in foreground to stop
> parallel ref updates. The remaining background operations (repack,
> prune and rerere) should impact running git processes.

Eck.. s/should impact/should not impact/
-- 
Duy
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH] wording fixes in the user manual and glossary

2014-05-24 Thread Jeremiah Mahler
Some minor wording fixes in the user manual and glossary.

Signed-off-by: Jeremiah Mahler 
---
 Documentation/glossary-content.txt | 2 +-
 Documentation/user-manual.txt  | 8 
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/Documentation/glossary-content.txt 
b/Documentation/glossary-content.txt
index be0858c..4e0b971 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -1,7 +1,7 @@
 [[def_alternate_object_database]]alternate object database::
Via the alternates mechanism, a <>
can inherit part of its <>
-   from another object database, which is called "alternate".
+   from another object database, which is called an "alternate".
 
 [[def_bare_repository]]bare repository::
A bare repository is normally an appropriately
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index d33f884..efb3c97 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -416,7 +416,7 @@ REVISIONS" section of linkgit:gitrevisions[7].
 Updating a repository with git fetch
 
 
-Eventually the developer cloned from will do additional work in her
+Eventually the developer will do additional work in her cloned
 repository, creating new commits and advancing the branches to point
 at the new commits.
 
@@ -1811,8 +1811,8 @@ manner.
 You can then import these into your mail client and send them by
 hand.  However, if you have a lot to send at once, you may prefer to
 use the linkgit:git-send-email[1] script to automate the process.
-Consult the mailing list for your project first to determine how they
-prefer such patches be handled.
+Consult the mailing list for your project first to determine
+their requirements for submitting patches.
 
 [[importing-patches]]
 Importing patches to a project
@@ -2255,7 +2255,7 @@ $ git checkout test && git merge speed-up-spinlocks
 It is unlikely that you would have any conflicts here ... but you might if you
 spent a while on this step and had also pulled new versions from upstream.
 
-Some time later when enough time has passed and testing done, you can pull the
+Sometime later when enough time has passed and testing done, you can pull the
 same branch into the `release` tree ready to go upstream.  This is where you
 see the value of keeping each patch (or patch series) in its own branch.  It
 means that the patches can be moved into the `release` tree in any order.
-- 
2.0.0.rc4.479.gd6c9c28

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v12 10/11] trailer: add tests for commands in config file

2014-05-24 Thread Christian Couder
And add a few other tests for some special cases.

Signed-off-by: Christian Couder 
Signed-off-by: Junio C Hamano 
---
 t/t7513-interpret-trailers.sh | 124 ++
 1 file changed, 124 insertions(+)

diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
index 9911c7b..d1e4970 100755
--- a/t/t7513-interpret-trailers.sh
+++ b/t/t7513-interpret-trailers.sh
@@ -441,4 +441,128 @@ test_expect_success 'using "ifMissing = doNothing"' '
test_cmp expected actual
 '
 
+test_expect_success 'with simple command' '
+   git config trailer.sign.key "Signed-off-by: " &&
+   git config trailer.sign.where "after" &&
+   git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+   git config trailer.sign.command "echo \"A U Thor 
\"" &&
+   cat complex_message_body >expected &&
+   sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+   Fixes: Z
+   Acked-by= Z
+   Reviewed-by:
+   Signed-off-by: Z
+   Signed-off-by: A U Thor 
+   EOF
+   git interpret-trailers --trailer "review:" --trailer "fix=22" \
+   actual &&
+   test_cmp expected actual
+'
+
+test_expect_success 'with command using commiter information' '
+   git config trailer.sign.ifExists "addIfDifferent" &&
+   git config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME 
<\$GIT_COMMITTER_EMAIL>\"" &&
+   cat complex_message_body >expected &&
+   sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+   Fixes: Z
+   Acked-by= Z
+   Reviewed-by:
+   Signed-off-by: Z
+   Signed-off-by: C O Mitter 
+   EOF
+   git interpret-trailers --trailer "review:" --trailer "fix=22" \
+   actual &&
+   test_cmp expected actual
+'
+
+test_expect_success 'with command using author information' '
+   git config trailer.sign.key "Signed-off-by: " &&
+   git config trailer.sign.where "after" &&
+   git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+   git config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME 
<\$GIT_AUTHOR_EMAIL>\"" &&
+   cat complex_message_body >expected &&
+   sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+   Fixes: Z
+   Acked-by= Z
+   Reviewed-by:
+   Signed-off-by: Z
+   Signed-off-by: A U Thor 
+   EOF
+   git interpret-trailers --trailer "review:" --trailer "fix=22" \
+   actual &&
+   test_cmp expected actual
+'
+
+test_expect_success 'setup a commit' '
+   echo "Content of the first commit." > a.txt &&
+   git add a.txt &&
+   git commit -m "Add file a.txt"
+'
+
+test_expect_success 'with command using $ARG' '
+   git config trailer.fix.ifExists "overwrite" &&
+   git config trailer.fix.command "git log -1 --oneline --format=\"%h 
(%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+   FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit 
--abbrev=14 HEAD) &&
+   cat complex_message_body >expected &&
+   sed -e "s/ Z\$/ /" >>expected <<-EOF &&
+   Fixes: $FIXED
+   Acked-by= Z
+   Reviewed-by:
+   Signed-off-by: Z
+   Signed-off-by: A U Thor 
+   EOF
+   git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+   actual &&
+   test_cmp expected actual
+'
+
+test_expect_success 'with failing command using $ARG' '
+   git config trailer.fix.ifExists "overwrite" &&
+   git config trailer.fix.command "false \$ARG" &&
+   cat complex_message_body >expected &&
+   sed -e "s/ Z\$/ /" >>expected <<-EOF &&
+   Fixes: Z
+   Acked-by= Z
+   Reviewed-by:
+   Signed-off-by: Z
+   Signed-off-by: A U Thor 
+   EOF
+   git interpret-trailers --trailer "review:" --trailer "fix=HEAD" \
+   actual &&
+   test_cmp expected actual
+'
+
+test_expect_success 'with empty tokens' '
+   cat >expected <<-EOF &&
+
+   Signed-off-by: A U Thor 
+   EOF
+   git interpret-trailers --trailer ":" --trailer ":test" >actual <<-EOF &&
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success 'with command but no key' '
+   git config --unset trailer.sign.key &&
+   cat >expected <<-EOF &&
+
+   sign: A U Thor 
+   EOF
+   git interpret-trailers >actual <<-EOF &&
+   EOF
+   test_cmp expected actual
+'
+
+test_expect_success 'with no command and no key' '
+   git config --unset trailer.review.key &&
+   cat >expected <<-EOF &&
+
+   review: Junio
+   sign: A U Thor 
+   EOF
+   git interpret-trailers --trailer "review:Junio" >actual <<-EOF &&
+   EOF
+   test_cmp expected actual
+'
+
 test_done
-- 
1.9.rc0.17.g651113e


--
To unsubscribe from this list: send the line "unsubscribe git" in
th

[PATCH v12 01/11] trailer: add data structures and basic functions

2014-05-24 Thread Christian Couder
We will use a doubly linked list to store all information
about trailers and their configuration.

This way we can easily remove or add trailers to or from
trailer lists while traversing the lists in either direction.

Signed-off-by: Christian Couder 
Signed-off-by: Junio C Hamano 
---
 Makefile  |  1 +
 trailer.c | 49 +
 2 files changed, 50 insertions(+)
 create mode 100644 trailer.c

diff --git a/Makefile b/Makefile
index b4af1e2..ec90feb 100644
--- a/Makefile
+++ b/Makefile
@@ -871,6 +871,7 @@ LIB_OBJS += submodule.o
 LIB_OBJS += symlinks.o
 LIB_OBJS += tag.o
 LIB_OBJS += trace.o
+LIB_OBJS += trailer.o
 LIB_OBJS += transport.o
 LIB_OBJS += transport-helper.o
 LIB_OBJS += tree-diff.o
diff --git a/trailer.c b/trailer.c
new file mode 100644
index 000..db93a63
--- /dev/null
+++ b/trailer.c
@@ -0,0 +1,49 @@
+#include "cache.h"
+/*
+ * Copyright (c) 2013, 2014 Christian Couder 
+ */
+
+enum action_where { WHERE_AFTER, WHERE_BEFORE };
+enum action_if_exists { EXISTS_ADD_IF_DIFFERENT, 
EXISTS_ADD_IF_DIFFERENT_NEIGHBOR,
+   EXISTS_ADD, EXISTS_OVERWRITE, EXISTS_DO_NOTHING };
+enum action_if_missing { MISSING_ADD, MISSING_DO_NOTHING };
+
+struct conf_info {
+   char *name;
+   char *key;
+   char *command;
+   enum action_where where;
+   enum action_if_exists if_exists;
+   enum action_if_missing if_missing;
+};
+
+struct trailer_item {
+   struct trailer_item *previous;
+   struct trailer_item *next;
+   const char *token;
+   const char *value;
+   struct conf_info conf;
+};
+
+static int same_token(struct trailer_item *a, struct trailer_item *b, int 
alnum_len)
+{
+   return !strncasecmp(a->token, b->token, alnum_len);
+}
+
+static int same_value(struct trailer_item *a, struct trailer_item *b)
+{
+   return !strcasecmp(a->value, b->value);
+}
+
+static int same_trailer(struct trailer_item *a, struct trailer_item *b, int 
alnum_len)
+{
+   return same_token(a, b, alnum_len) && same_value(a, b);
+}
+
+/* Get the length of buf from its beginning until its last alphanumeric 
character */
+static size_t alnum_len(const char *buf, size_t len)
+{
+   while (len > 0 && !isalnum(buf[len - 1]))
+   len--;
+   return len;
+}
-- 
1.9.rc0.17.g651113e


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v12 08/11] trailer: add tests for "git interpret-trailers"

2014-05-24 Thread Christian Couder
Signed-off-by: Christian Couder 
Signed-off-by: Junio C Hamano 
---
 t/t7513-interpret-trailers.sh | 444 ++
 1 file changed, 444 insertions(+)
 create mode 100755 t/t7513-interpret-trailers.sh

diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh
new file mode 100755
index 000..9911c7b
--- /dev/null
+++ b/t/t7513-interpret-trailers.sh
@@ -0,0 +1,444 @@
+#!/bin/sh
+#
+# Copyright (c) 2013, 2014 Christian Couder
+#
+
+test_description='git interpret-trailers'
+
+. ./test-lib.sh
+
+# When we want one trailing space at the end of each line, let's use sed
+# to make sure that these spaces are not removed by any automatic tool.
+
+test_expect_success 'setup' '
+   cat >basic_message <<-\EOF &&
+   subject
+
+   body
+   EOF
+   cat >complex_message_body <<-\EOF &&
+   my subject
+
+   my body which is long
+   and contains some special
+   chars like : = ? !
+
+   EOF
+   sed -e "s/ Z\$/ /" >complex_message_trailers <<-\EOF &&
+   Fixes: Z
+   Acked-by: Z
+   Reviewed-by: Z
+   Signed-off-by: Z
+   EOF
+   cat >basic_patch <<-\EOF
+   ---
+foo.txt | 2 +-
+1 file changed, 1 insertion(+), 1 deletion(-)
+
+   diff --git a/foo.txt b/foo.txt
+   index 0353767..1d91aa1 100644
+   --- a/foo.txt
+   +++ b/foo.txt
+   @@ -1,3 +1,3 @@
+
+   -bar
+   +baz
+
+   --
+   1.9.rc0.11.ga562ddc
+
+   EOF
+'
+
+test_expect_success 'without config' '
+   sed -e "s/ Z\$/ /" >expected <<-\EOF &&
+
+   ack: Peff
+   Reviewed-by: Z
+   Acked-by: Johan
+   EOF
+   git interpret-trailers --trailer "ack = Peff" --trailer "Reviewed-by" \
+   --trailer "Acked-by: Johan" >actual &&
+   test_cmp expected actual
+'
+
+test_expect_success '--trim-empty without config' '
+   cat >expected <<-\EOF &&
+
+   ack: Peff
+   Acked-by: Johan
+   EOF
+   git interpret-trailers --trim-empty --trailer "ack = Peff" \
+   --trailer "Reviewed-by" --trailer "Acked-by: Johan" \
+   --trailer "sob:" >actual &&
+   test_cmp expected actual
+'
+
+test_expect_success 'with config setup' '
+   git config trailer.ack.key "Acked-by: " &&
+   cat >expected <<-\EOF &&
+
+   Acked-by: Peff
+   EOF
+   git interpret-trailers --trim-empty --trailer "ack = Peff" >actual &&
+   test_cmp expected actual &&
+   git interpret-trailers --trim-empty --trailer "Acked-by = Peff" >actual 
&&
+   test_cmp expected actual &&
+   git interpret-trailers --trim-empty --trailer "Acked-by :Peff" >actual 
&&
+   test_cmp expected actual
+'
+
+test_expect_success 'with config setup and = sign' '
+   git config trailer.ack.key "Acked-by= " &&
+   cat >expected <<-\EOF &&
+
+   Acked-by= Peff
+   EOF
+   git interpret-trailers --trim-empty --trailer "ack = Peff" >actual &&
+   test_cmp expected actual &&
+   git interpret-trailers --trim-empty --trailer "Acked-by= Peff" >actual 
&&
+   test_cmp expected actual &&
+   git interpret-trailers --trim-empty --trailer "Acked-by : Peff" >actual 
&&
+   test_cmp expected actual
+'
+
+test_expect_success 'with config setup and # sign' '
+   git config trailer.bug.key "Bug #" &&
+   cat >expected <<-\EOF &&
+
+   Bug #42
+   EOF
+   git interpret-trailers --trim-empty --trailer "bug = 42" >actual &&
+   test_cmp expected actual
+'
+
+test_expect_success 'with commit basic message' '
+   cat basic_message >expected &&
+   echo >>expected &&
+   git interpret-trailers actual &&
+   test_cmp expected actual
+'
+
+test_expect_success 'with basic patch' '
+   cat basic_message >input &&
+   cat basic_patch >>input &&
+   cat basic_message >expected &&
+   echo >>expected &&
+   cat basic_patch >>expected &&
+   git interpret-trailers actual &&
+   test_cmp expected actual
+'
+
+test_expect_success 'with commit complex message as argument' '
+   cat complex_message_body complex_message_trailers >complex_message &&
+   cat complex_message_body >expected &&
+   sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+   Fixes: Z
+   Acked-by= Z
+   Reviewed-by: Z
+   Signed-off-by: Z
+   EOF
+   git interpret-trailers complex_message >actual &&
+   test_cmp expected actual
+'
+
+test_expect_success 'with 2 files arguments' '
+   cat basic_message >>expected &&
+   echo >>expected &&
+   cat basic_patch >>expected &&
+   git interpret-trailers complex_message input >actual &&
+   test_cmp expected actual
+'
+
+test_expect_succes

[PATCH v12 02/11] trailer: process trailers from input message and arguments

2014-05-24 Thread Christian Couder
Implement the logic to process trailers from the input message
and from arguments.

At the beginning trailers from the input message are in their
own "in_tok" doubly linked list, and trailers from arguments
are in their own "arg_tok" doubly linked list.

The lists are traversed and when an "arg_tok" should be "applied",
it is removed from its list and inserted into the "in_tok" list.

Signed-off-by: Christian Couder 
Signed-off-by: Junio C Hamano 
---
 trailer.c | 198 ++
 1 file changed, 198 insertions(+)

diff --git a/trailer.c b/trailer.c
index db93a63..52108c2 100644
--- a/trailer.c
+++ b/trailer.c
@@ -47,3 +47,201 @@ static size_t alnum_len(const char *buf, size_t len)
len--;
return len;
 }
+
+static void free_trailer_item(struct trailer_item *item)
+{
+   free(item->conf.name);
+   free(item->conf.key);
+   free(item->conf.command);
+   free((char *)item->token);
+   free((char *)item->value);
+   free(item);
+}
+
+static void add_arg_to_input_list(struct trailer_item *in_tok,
+ struct trailer_item *arg_tok)
+{
+   if (arg_tok->conf.where == WHERE_AFTER) {
+   arg_tok->next = in_tok->next;
+   in_tok->next = arg_tok;
+   arg_tok->previous = in_tok;
+   if (arg_tok->next)
+   arg_tok->next->previous = arg_tok;
+   } else {
+   arg_tok->previous = in_tok->previous;
+   in_tok->previous = arg_tok;
+   arg_tok->next = in_tok;
+   if (arg_tok->previous)
+   arg_tok->previous->next = arg_tok;
+   }
+}
+
+static int check_if_different(struct trailer_item *in_tok,
+ struct trailer_item *arg_tok,
+ int alnum_len, int check_all)
+{
+   enum action_where where = arg_tok->conf.where;
+   do {
+   if (!in_tok)
+   return 1;
+   if (same_trailer(in_tok, arg_tok, alnum_len))
+   return 0;
+   /*
+* if we want to add a trailer after another one,
+* we have to check those before this one
+*/
+   in_tok = (where == WHERE_AFTER) ? in_tok->previous : 
in_tok->next;
+   } while (check_all);
+   return 1;
+}
+
+static void apply_arg_if_exists(struct trailer_item *in_tok,
+   struct trailer_item *arg_tok,
+   int alnum_len)
+{
+   switch (arg_tok->conf.if_exists) {
+   case EXISTS_DO_NOTHING:
+   free_trailer_item(arg_tok);
+   break;
+   case EXISTS_OVERWRITE:
+   free((char *)in_tok->value);
+   in_tok->value = xstrdup(arg_tok->value);
+   free_trailer_item(arg_tok);
+   break;
+   case EXISTS_ADD:
+   add_arg_to_input_list(in_tok, arg_tok);
+   break;
+   case EXISTS_ADD_IF_DIFFERENT:
+   if (check_if_different(in_tok, arg_tok, alnum_len, 1))
+   add_arg_to_input_list(in_tok, arg_tok);
+   else
+   free_trailer_item(arg_tok);
+   break;
+   case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:
+   if (check_if_different(in_tok, arg_tok, alnum_len, 0))
+   add_arg_to_input_list(in_tok, arg_tok);
+   else
+   free_trailer_item(arg_tok);
+   break;
+   }
+}
+
+static void remove_from_list(struct trailer_item *item,
+struct trailer_item **first)
+{
+   if (item->next)
+   item->next->previous = item->previous;
+   if (item->previous)
+   item->previous->next = item->next;
+   else
+   *first = item->next;
+}
+
+static struct trailer_item *remove_first(struct trailer_item **first)
+{
+   struct trailer_item *item = *first;
+   *first = item->next;
+   if (item->next) {
+   item->next->previous = NULL;
+   item->next = NULL;
+   }
+   return item;
+}
+
+static void process_input_token(struct trailer_item *in_tok,
+   struct trailer_item **arg_tok_first,
+   enum action_where where)
+{
+   struct trailer_item *arg_tok;
+   struct trailer_item *next_arg;
+
+   int after = where == WHERE_AFTER;
+   int tok_alnum_len = alnum_len(in_tok->token, strlen(in_tok->token));
+
+   for (arg_tok = *arg_tok_first; arg_tok; arg_tok = next_arg) {
+   next_arg = arg_tok->next;
+   if (!same_token(in_tok, arg_tok, tok_alnum_len))
+   continue;
+   if (arg_tok->conf.where != where)
+   continue;
+   remove_from_list(arg_tok, arg_tok_first);
+   apply_arg_if_e

[PATCH v12 00/11] Add interpret-trailers builtin

2014-05-24 Thread Christian Couder
This patch series implements a new command:

git interpret-trailers

and an infrastructure to process trailers that can be reused,
for example in "commit.c".

1) Rationale:

This command should help with RFC 822 style headers, called
"trailers", that are found at the end of commit messages.

(Note that these headers do not follow and are not intended to
follow many rules that are in RFC 822. For example they do not
follow the line breaking rules, the encoding rules and probably
many other rules.)

For a long time, these trailers have become a de facto standard
way to add helpful information into commit messages.

Until now git commit has only supported the well known
"Signed-off-by: " trailer, that is used by many projects like
the Linux kernel and Git.

It is better to keep builtin/commit.c uncontaminated by any more
hard-wired logic, like what we have for the signed-off-by line.  Any
new things can and should be doable in hooks, and this filter would
help writing these hooks.

And that is why the design goal of the filter is to make it at least
as powerful as the built-in logic we have for signed-off-by lines;
that would allow us to later eject the hard-wired logic for
signed-off-by line from the main codepath, if/when we wanted to.

Alternatively, we could build a library-ish API around this filter
code and replace the hard-wired logic for signed-off-by line with a
call into that API, if/when we wanted to, but that requires (in
addition to the "at least as powerful as the built-in logic") that
the implementation of this stand-alone filter can be cleanly made
into a reusable library, so that is a bit higher bar to cross than
"everything can be doable with hooks" alternative.

2) Current state:

Currently the usage string of this command is:

git interpret-trailers [--trim-empty] [(--trailer [(=|:)])...] 
[...]

The following features are implemented:

- the result is printed on stdout
- the --trailer arguments are interpreted
- messages read from ... or stdin are interpreted
- the "trailer..key" options in the config are interpreted
- the "trailer..where" options are interpreted
- the "trailer..ifExist" options are interpreted
- the "trailer..ifMissing" options are interpreted
- the "trailer..command" config works
- $ARG can be used in commands
- messages can contain a patch
- lines in messages starting with a comment char are ignored (new)
- there are 35 tests
- there is some documentation
- there are examples in the documentation (new)

Possible improvements:
- integration with "git commit"
- support GIT_COMMIT_PROTO env variable in commands

3) Changes since version 11, thanks to Michael, Jonathan and Junio:

* the patch to ignore comments in messages has been squashed into
  the original series (5/11 and 8/11)
* the comment char config variable is used to ignore comments in
  messages (as suggested by Michael) (5/11)
* the patch that add examples in the documentation has been
  squashed into the original (11/11)
* one of the examples has been split into 2 examples (as suggested
  by Junio) (11/11)
* there is no special case for # when printing the trailers, (as
  suggested by Junio); so we always use ': ' as separator, except
  when trailer..key ends with a non alphanumeric character;
  in that case we add no separator as we suppose that the key
  already contains a separator (6/11, 8/11 and 10/11)
* documentation has been improved to be clearer, and more
  explicit, especially about how the key config variable is
  used (11/11)

Christian Couder (11):
  trailer: add data structures and basic functions
  trailer: process trailers from input message and arguments
  trailer: read and process config information
  trailer: process command line trailer arguments
  trailer: parse trailers from file or stdin
  trailer: put all the processing together and print
  trailer: add interpret-trailers command
  trailer: add tests for "git interpret-trailers"
  trailer: execute command from 'trailer..command'
  trailer: add tests for commands in config file
  Documentation: add documentation for 'git interpret-trailers'

 .gitignore   |   1 +
 Documentation/git-interpret-trailers.txt | 264 +++
 Makefile |   2 +
 builtin.h|   1 +
 builtin/interpret-trailers.c |  44 ++
 command-list.txt |   1 +
 git.c|   1 +
 t/t7513-interpret-trailers.sh| 568 +++
 trailer.c| 755 +++
 trailer.h|   6 +
 10 files changed, 1643 insertions(+)
 create mode 100644 Documentation/git-interpret-trailers.txt
 create mode 100644 builtin/interpret-trailers.c
 create mode 100755 t/t7513-interpret-trailers.sh
 create mode 10

[PATCH v12 11/11] Documentation: add documentation for 'git interpret-trailers'

2014-05-24 Thread Christian Couder
While at it add git-interpret-trailers to "command-list.txt".

Signed-off-by: Christian Couder 
Signed-off-by: Junio C Hamano 
---
 Documentation/git-interpret-trailers.txt | 264 +++
 command-list.txt |   1 +
 2 files changed, 265 insertions(+)
 create mode 100644 Documentation/git-interpret-trailers.txt

diff --git a/Documentation/git-interpret-trailers.txt 
b/Documentation/git-interpret-trailers.txt
new file mode 100644
index 000..45f1ed4
--- /dev/null
+++ b/Documentation/git-interpret-trailers.txt
@@ -0,0 +1,264 @@
+git-interpret-trailers(1)
+=
+
+NAME
+
+git-interpret-trailers - help add stuctured information into commit messages
+
+SYNOPSIS
+
+[verse]
+'git interpret-trailers' [--trim-empty] [(--trailer [(=|:)])...] 
[...]
+
+DESCRIPTION
+---
+Help adding 'trailers' lines, that look similar to RFC 822 e-mail
+headers, at the end of the otherwise free-form part of a commit
+message.
+
+This command reads some patches or commit messages from either the
+ arguments or the standard input if no  is specified. Then
+this command applies the arguments passed using the `--trailer`
+option, if any, to the commit message part of each input file. The
+result is emitted on the standard output.
+
+Some configuration variables control the way the `--trailer` arguments
+are applied to each commit message and the way any existing trailer in
+the commit message is changed. They also make it possible to
+automatically add some trailers.
+
+By default, a '=' or ':' argument given
+using `--trailer` will be added only if no trailer with the same
+(, ) pair is already in the message. The  and
+ parts will be trimmed to remove starting and trailing
+whitespace, and the resulting trimmed  and  will appear
+in the message like this:
+
+
+token: value
+
+
+This means that the trimmed  and  will be separated by
+`': '` (one colon followed by one space).
+
+By default, if there are already trailers with the same , the
+new trailer will appear just after the last trailer with the same
+. Otherwise it will appear at the end of the commit message
+part of the ouput.
+
+The trailers are recognized in the input message using the following
+rules:
+
+* only lines that contains a ':' (colon) are considered trailers,
+
+* the trailer lines must all be next to each other,
+
+* after them it's only possible to have some lines that contain only
+  spaces, and then a patch; the patch part is recognized using the
+  fact that its first line starts with '---' (three minus signs),
+
+* before them there must be at least one line with only spaces.
+
+Note that 'trailers' do not follow and are not intended to follow many
+rules for RFC 822 headers. For example they do not follow the line
+folding rules, the encoding rules and probably many other rules.
+
+OPTIONS
+---
+--trim-empty::
+   If the  part of any trailer contains only whitespace,
+   the whole trailer will be removed from the resulting message.
+   This apply to existing trailers as well as new trailers.
+
+--trailer [(=|:)]::
+   Specify a (, ) pair that should be applied as a
+   trailer to the input messages. See the description of this
+   command.
+
+CONFIGURATION VARIABLES
+---
+
+trailer..key::
+   This `key` will be used instead of  in the
+   trailer. After the last alphanumeric character, this variable
+   can contain some non alphanumeric characters, like for example
+   `'%'` (one percent sign), `' = '` (one space followed by one
+   equal sign and then one space), `' #'` (one space followed by
+   one number sign) or even just `' '` (one space), that will be
+   used to separate the  from the  in the
+   trailer. The default separator, `': '` (one colon followed by
+   one space), which is the usual standard, is added only if the
+   last character in `key` is alphanumeric, so watch out for
+   unwanted trailing spaces in this variable.
+
+trailer..where::
+   This can be either `after`, which is the default, or
+   `before`. If it is `before`, then a trailer with the specified
+   , will appear before, instead of after, other trailers
+   with the same , or otherwise at the beginning, instead
+   of at the end, of all the trailers.
+
+trailer..ifexist::
+   This option makes it possible to choose what action will be
+   performed when there is already at least one trailer with the
+   same  in the message.
++
+The valid values for this option are: `addIfDifferent` (this is the
+default), `addIfDifferentNeighbor`, `add`, `overwrite` or `doNothing`.
++
+With `addIfDifferent`, a new trailer will be added only if no trailer
+with the same (, ) pair is already in the message.
++
+With `addIfDifferentNeighbor`, a new trailer will be added only if no
+trailer wit

[PATCH v12 03/11] trailer: read and process config information

2014-05-24 Thread Christian Couder
Read the configuration to get trailer information, and then process
it and store it in a doubly linked list.

The config information is stored in the list whose first item is
pointed to by:

static struct trailer_item *first_conf_item;

Signed-off-by: Christian Couder 
Signed-off-by: Junio C Hamano 
---
 trailer.c | 146 ++
 1 file changed, 146 insertions(+)

diff --git a/trailer.c b/trailer.c
index 52108c2..f376be5 100644
--- a/trailer.c
+++ b/trailer.c
@@ -25,6 +25,8 @@ struct trailer_item {
struct conf_info conf;
 };
 
+static struct trailer_item *first_conf_item;
+
 static int same_token(struct trailer_item *a, struct trailer_item *b, int 
alnum_len)
 {
return !strncasecmp(a->token, b->token, alnum_len);
@@ -245,3 +247,147 @@ static void process_trailers_lists(struct trailer_item 
**in_tok_first,
apply_arg_if_missing(in_tok_first, in_tok_last, arg_tok);
}
 }
+
+static int set_where(struct conf_info *item, const char *value)
+{
+   if (!strcasecmp("after", value))
+   item->where = WHERE_AFTER;
+   else if (!strcasecmp("before", value))
+   item->where = WHERE_BEFORE;
+   else
+   return -1;
+   return 0;
+}
+
+static int set_if_exists(struct conf_info *item, const char *value)
+{
+   if (!strcasecmp("addIfDifferent", value))
+   item->if_exists = EXISTS_ADD_IF_DIFFERENT;
+   else if (!strcasecmp("addIfDifferentNeighbor", value))
+   item->if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
+   else if (!strcasecmp("add", value))
+   item->if_exists = EXISTS_ADD;
+   else if (!strcasecmp("overwrite", value))
+   item->if_exists = EXISTS_OVERWRITE;
+   else if (!strcasecmp("doNothing", value))
+   item->if_exists = EXISTS_DO_NOTHING;
+   else
+   return -1;
+   return 0;
+}
+
+static int set_if_missing(struct conf_info *item, const char *value)
+{
+   if (!strcasecmp("doNothing", value))
+   item->if_missing = MISSING_DO_NOTHING;
+   else if (!strcasecmp("add", value))
+   item->if_missing = MISSING_ADD;
+   else
+   return -1;
+   return 0;
+}
+
+static struct trailer_item *get_conf_item(const char *name)
+{
+   struct trailer_item *item;
+   struct trailer_item *previous;
+
+   /* Look up item with same name */
+   for (previous = NULL, item = first_conf_item;
+item;
+previous = item, item = item->next) {
+   if (!strcasecmp(item->conf.name, name))
+   return item;
+   }
+
+   /* Item does not already exists, create it */
+   item = xcalloc(sizeof(struct trailer_item), 1);
+   item->conf.name = xstrdup(name);
+
+   if (!previous)
+   first_conf_item = item;
+   else {
+   previous->next = item;
+   item->previous = previous;
+   }
+
+   return item;
+}
+
+enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_WHERE,
+TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
+
+static struct {
+   const char *name;
+   enum trailer_info_type type;
+} trailer_config_items[] = {
+   { "key", TRAILER_KEY },
+   { "command", TRAILER_COMMAND },
+   { "where", TRAILER_WHERE },
+   { "ifexists", TRAILER_IF_EXISTS },
+   { "ifmissing", TRAILER_IF_MISSING }
+};
+
+static int git_trailer_config(const char *conf_key, const char *value, void 
*cb)
+{
+   const char *trailer_item, *variable_name;
+   struct trailer_item *item;
+   struct conf_info *conf;
+   char *name = NULL;
+   enum trailer_info_type type;
+   int i;
+
+   trailer_item = skip_prefix(conf_key, "trailer.");
+   if (!trailer_item)
+   return 0;
+
+   variable_name = strrchr(trailer_item, '.');
+   if (!variable_name) {
+   warning(_("two level trailer config variable %s"), conf_key);
+   return 0;
+   }
+
+   variable_name++;
+   for (i = 0; i < ARRAY_SIZE(trailer_config_items); i++) {
+   if (strcmp(trailer_config_items[i].name, variable_name))
+   continue;
+   name = xstrndup(trailer_item,  variable_name - trailer_item - 
1);
+   type = trailer_config_items[i].type;
+   break;
+   }
+
+   if (!name)
+   return 0;
+
+   item = get_conf_item(name);
+   conf = &item->conf;
+   free(name);
+
+   switch (type) {
+   case TRAILER_KEY:
+   if (conf->key)
+   warning(_("more than one %s"), conf_key);
+   conf->key = xstrdup(value);
+   break;
+   case TRAILER_COMMAND:
+   if (conf->command)
+   warning(_("more than one %s"), conf_key);
+   conf->command = xstrdup(value);
+   br

[PATCH v12 07/11] trailer: add interpret-trailers command

2014-05-24 Thread Christian Couder
This patch adds the "git interpret-trailers" command.
This command uses the previously added process_trailers()
function in trailer.c.

Signed-off-by: Christian Couder 
Signed-off-by: Junio C Hamano 
---
 .gitignore   |  1 +
 Makefile |  1 +
 builtin.h|  1 +
 builtin/interpret-trailers.c | 44 
 git.c|  1 +
 5 files changed, 48 insertions(+)
 create mode 100644 builtin/interpret-trailers.c

diff --git a/.gitignore b/.gitignore
index b5f9def..c870ada 100644
--- a/.gitignore
+++ b/.gitignore
@@ -74,6 +74,7 @@
 /git-index-pack
 /git-init
 /git-init-db
+/git-interpret-trailers
 /git-instaweb
 /git-log
 /git-ls-files
diff --git a/Makefile b/Makefile
index ec90feb..a91465e 100644
--- a/Makefile
+++ b/Makefile
@@ -935,6 +935,7 @@ BUILTIN_OBJS += builtin/hash-object.o
 BUILTIN_OBJS += builtin/help.o
 BUILTIN_OBJS += builtin/index-pack.o
 BUILTIN_OBJS += builtin/init-db.o
+BUILTIN_OBJS += builtin/interpret-trailers.o
 BUILTIN_OBJS += builtin/log.o
 BUILTIN_OBJS += builtin/ls-files.o
 BUILTIN_OBJS += builtin/ls-remote.o
diff --git a/builtin.h b/builtin.h
index c47c110..8ca0065 100644
--- a/builtin.h
+++ b/builtin.h
@@ -73,6 +73,7 @@ extern int cmd_hash_object(int argc, const char **argv, const 
char *prefix);
 extern int cmd_help(int argc, const char **argv, const char *prefix);
 extern int cmd_index_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_init_db(int argc, const char **argv, const char *prefix);
+extern int cmd_interpret_trailers(int argc, const char **argv, const char 
*prefix);
 extern int cmd_log(int argc, const char **argv, const char *prefix);
 extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c
new file mode 100644
index 000..46838d2
--- /dev/null
+++ b/builtin/interpret-trailers.c
@@ -0,0 +1,44 @@
+/*
+ * Builtin "git interpret-trailers"
+ *
+ * Copyright (c) 2013, 2014 Christian Couder 
+ *
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "trailer.h"
+
+static const char * const git_interpret_trailers_usage[] = {
+   N_("git interpret-trailers [--trim-empty] [(--trailer 
[(=|:)])...] [...]"),
+   NULL
+};
+
+int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
+{
+   int trim_empty = 0;
+   struct string_list trailers = STRING_LIST_INIT_DUP;
+
+   struct option options[] = {
+   OPT_BOOL(0, "trim-empty", &trim_empty, N_("trim empty 
trailers")),
+   OPT_STRING_LIST(0, "trailer", &trailers, N_("trailer"),
+   N_("trailer(s) to add")),
+   OPT_END()
+   };
+
+   argc = parse_options(argc, argv, prefix, options,
+git_interpret_trailers_usage, 0);
+
+   if (argc) {
+   int i;
+   for (i = 0; i < argc; i++)
+   process_trailers(argv[i], trim_empty, &trailers);
+   } else
+   process_trailers(NULL, trim_empty, &trailers);
+
+   string_list_clear(&trailers, 0);
+
+   return 0;
+}
diff --git a/git.c b/git.c
index 7cf2953..63a03eb 100644
--- a/git.c
+++ b/git.c
@@ -380,6 +380,7 @@ static struct cmd_struct commands[] = {
{ "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
{ "init", cmd_init_db },
{ "init-db", cmd_init_db },
+   { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP },
{ "log", cmd_log, RUN_SETUP },
{ "ls-files", cmd_ls_files, RUN_SETUP },
{ "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
-- 
1.9.rc0.17.g651113e


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v12 05/11] trailer: parse trailers from file or stdin

2014-05-24 Thread Christian Couder
Read trailers from a file or from stdin, parse the trailers and then
put the result into a doubly linked list.

Signed-off-by: Christian Couder 
Signed-off-by: Junio C Hamano 
---
 trailer.c | 123 ++
 1 file changed, 123 insertions(+)

diff --git a/trailer.c b/trailer.c
index f79a369..40ad1a1 100644
--- a/trailer.c
+++ b/trailer.c
@@ -51,6 +51,14 @@ static size_t alnum_len(const char *buf, size_t len)
return len;
 }
 
+static inline int contains_only_spaces(const char *str)
+{
+   const char *s = str;
+   while (*s && isspace(*s))
+   s++;
+   return !*s;
+}
+
 static void free_trailer_item(struct trailer_item *item)
 {
free(item->conf.name);
@@ -509,3 +517,118 @@ static struct trailer_item 
*process_command_line_args(struct string_list *traile
 
return arg_tok_first;
 }
+
+static struct strbuf **read_input_file(const char *file)
+{
+   struct strbuf **lines;
+   struct strbuf sb = STRBUF_INIT;
+
+   if (file) {
+   if (strbuf_read_file(&sb, file, 0) < 0)
+   die_errno(_("could not read input file '%s'"), file);
+   } else {
+   if (strbuf_read(&sb, fileno(stdin), 0) < 0)
+   die_errno(_("could not read from stdin"));
+   }
+
+   lines = strbuf_split(&sb, '\n');
+
+   strbuf_release(&sb);
+
+   return lines;
+}
+
+/*
+ * Return the (0 based) index of the start of the patch or the line
+ * count if there is no patch in the message.
+ */
+static int find_patch_start(struct strbuf **lines, int count)
+{
+   int i;
+
+   /* Get the start of the patch part if any */
+   for (i = 0; i < count; i++) {
+   if (starts_with(lines[i]->buf, "---"))
+   return i;
+   }
+
+   return count;
+}
+
+/*
+ * Return the (0 based) index of the first trailer line or count if
+ * there are no trailers. Trailers are searched only in the lines from
+ * index (count - 1) down to index 0.
+ */
+static int find_trailer_start(struct strbuf **lines, int count)
+{
+   int start, only_spaces = 1;
+
+   /*
+* Get the start of the trailers by looking starting from the end
+* for a line with only spaces before lines with one ':'.
+*/
+   for (start = count - 1; start >= 0; start--) {
+   if (lines[start]->buf[0] == comment_line_char)
+   continue;
+   if (contains_only_spaces(lines[start]->buf)) {
+   if (only_spaces)
+   continue;
+   return start + 1;
+   }
+   if (strchr(lines[start]->buf, ':')) {
+   if (only_spaces)
+   only_spaces = 0;
+   continue;
+   }
+   return count;
+   }
+
+   return only_spaces ? count : 0;
+}
+
+static int has_blank_line_before(struct strbuf **lines, int start)
+{
+   for (;start >= 0; start--) {
+   if (lines[start]->buf[0] == comment_line_char)
+   continue;
+   return contains_only_spaces(lines[start]->buf);
+   }
+   return 0;
+}
+
+static void print_lines(struct strbuf **lines, int start, int end)
+{
+   int i;
+   for (i = start; lines[i] && i < end; i++)
+   printf("%s", lines[i]->buf);
+}
+
+static int process_input_file(struct strbuf **lines,
+ struct trailer_item **in_tok_first,
+ struct trailer_item **in_tok_last)
+{
+   int count = 0;
+   int patch_start, trailer_start, i;
+
+   /* Get the line count */
+   while (lines[count])
+   count++;
+
+   patch_start = find_patch_start(lines, count);
+   trailer_start = find_trailer_start(lines, patch_start);
+
+   /* Print lines before the trailers as is */
+   print_lines(lines, 0, trailer_start);
+
+   if (!has_blank_line_before(lines, trailer_start - 1))
+   printf("\n");
+
+   /* Parse trailer lines */
+   for (i = trailer_start; i < patch_start; i++) {
+   struct trailer_item *new = create_trailer_item(lines[i]->buf);
+   add_trailer_item(in_tok_first, in_tok_last, new);
+   }
+
+   return patch_start;
+}
-- 
1.9.rc0.17.g651113e


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v12 09/11] trailer: execute command from 'trailer..command'

2014-05-24 Thread Christian Couder
Let the user specify a command that will give on its standard output
the value to use for the specified trailer.

Signed-off-by: Christian Couder 
Signed-off-by: Junio C Hamano 
---
 trailer.c | 65 +++
 1 file changed, 65 insertions(+)

diff --git a/trailer.c b/trailer.c
index d648939..eaf692b 100644
--- a/trailer.c
+++ b/trailer.c
@@ -1,5 +1,7 @@
 #include "cache.h"
 #include "string-list.h"
+#include "run-command.h"
+#include "string-list.h"
 #include "trailer.h"
 /*
  * Copyright (c) 2013, 2014 Christian Couder 
@@ -14,11 +16,14 @@ struct conf_info {
char *name;
char *key;
char *command;
+   unsigned command_uses_arg : 1;
enum action_where where;
enum action_if_exists if_exists;
enum action_if_missing if_missing;
 };
 
+#define TRAILER_ARG_STRING "$ARG"
+
 struct trailer_item {
struct trailer_item *previous;
struct trailer_item *next;
@@ -60,6 +65,13 @@ static inline int contains_only_spaces(const char *str)
return !*s;
 }
 
+static inline void strbuf_replace(struct strbuf *sb, const char *a, const char 
*b)
+{
+   const char *ptr = strstr(sb->buf, a);
+   if (ptr)
+   strbuf_splice(sb, ptr - sb->buf, strlen(a), b, strlen(b));
+}
+
 static void free_trailer_item(struct trailer_item *item)
 {
free(item->conf.name);
@@ -401,6 +413,7 @@ static int git_trailer_config(const char *conf_key, const 
char *value, void *cb)
if (conf->command)
warning(_("more than one %s"), conf_key);
conf->command = xstrdup(value);
+   conf->command_uses_arg = !!strstr(conf->command, 
TRAILER_ARG_STRING);
break;
case TRAILER_WHERE:
if (set_where(conf, value))
@@ -437,6 +450,45 @@ static int parse_trailer(struct strbuf *tok, struct strbuf 
*val, const char *tra
return 0;
 }
 
+static int read_from_command(struct child_process *cp, struct strbuf *buf)
+{
+   if (run_command(cp))
+   return error("running trailer command '%s' failed", 
cp->argv[0]);
+   if (strbuf_read(buf, cp->out, 1024) < 1)
+   return error("reading from trailer command '%s' failed", 
cp->argv[0]);
+   strbuf_trim(buf);
+   return 0;
+}
+
+static const char *apply_command(const char *command, const char *arg)
+{
+   struct strbuf cmd = STRBUF_INIT;
+   struct strbuf buf = STRBUF_INIT;
+   struct child_process cp;
+   const char *argv[] = {NULL, NULL};
+   const char *result;
+
+   strbuf_addstr(&cmd, command);
+   if (arg)
+   strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
+
+   argv[0] = cmd.buf;
+   memset(&cp, 0, sizeof(cp));
+   cp.argv = argv;
+   cp.env = local_repo_env;
+   cp.no_stdin = 1;
+   cp.out = -1;
+   cp.use_shell = 1;
+
+   if (read_from_command(&cp, &buf)) {
+   strbuf_release(&buf);
+   result = xstrdup("");
+   } else
+   result = strbuf_detach(&buf, NULL);
+
+   strbuf_release(&cmd);
+   return result;
+}
 
 static void duplicate_conf(struct conf_info *dst, struct conf_info *src)
 {
@@ -467,6 +519,10 @@ static struct trailer_item *new_trailer_item(struct 
trailer_item *conf_item,
duplicate_conf(&new->conf, &conf_item->conf);
new->token = xstrdup(token_from_item(conf_item));
free(tok);
+   if (conf_item->conf.command_uses_arg || !val) {
+   new->value = apply_command(conf_item->conf.command, 
val);
+   free(val);
+   }
} else
new->token = tok;
 
@@ -528,12 +584,21 @@ static struct trailer_item 
*process_command_line_args(struct string_list *traile
struct trailer_item *arg_tok_first = NULL;
struct trailer_item *arg_tok_last = NULL;
struct string_list_item *tr;
+   struct trailer_item *item;
 
for_each_string_list_item(tr, trailers) {
struct trailer_item *new = create_trailer_item(tr->string);
add_trailer_item(&arg_tok_first, &arg_tok_last, new);
}
 
+   /* Add conf commands that don't use $ARG */
+   for (item = first_conf_item; item; item = item->next) {
+   if (item->conf.command && !item->conf.command_uses_arg) {
+   struct trailer_item *new = new_trailer_item(item, NULL, 
NULL);
+   add_trailer_item(&arg_tok_first, &arg_tok_last, new);
+   }
+   }
+
return arg_tok_first;
 }
 
-- 
1.9.rc0.17.g651113e


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v12 04/11] trailer: process command line trailer arguments

2014-05-24 Thread Christian Couder
Parse the trailer command line arguments and put
the result into an arg_tok doubly linked list.

Signed-off-by: Christian Couder 
Signed-off-by: Junio C Hamano 
---
 trailer.c | 118 ++
 1 file changed, 118 insertions(+)

diff --git a/trailer.c b/trailer.c
index f376be5..f79a369 100644
--- a/trailer.c
+++ b/trailer.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "string-list.h"
 /*
  * Copyright (c) 2013, 2014 Christian Couder 
  */
@@ -391,3 +392,120 @@ static int git_trailer_config(const char *conf_key, const 
char *value, void *cb)
}
return 0;
 }
+
+static int parse_trailer(struct strbuf *tok, struct strbuf *val, const char 
*trailer)
+{
+   size_t len = strcspn(trailer, "=:");
+   if (len == 0)
+   return error(_("empty trailer token in trailer '%s'"), trailer);
+   if (len < strlen(trailer)) {
+   strbuf_add(tok, trailer, len);
+   strbuf_trim(tok);
+   strbuf_addstr(val, trailer + len + 1);
+   strbuf_trim(val);
+   } else {
+   strbuf_addstr(tok, trailer);
+   strbuf_trim(tok);
+   }
+   return 0;
+}
+
+
+static void duplicate_conf(struct conf_info *dst, struct conf_info *src)
+{
+   *dst = *src;
+   if (src->name)
+   dst->name = xstrdup(src->name);
+   if (src->key)
+   dst->key = xstrdup(src->key);
+   if (src->command)
+   dst->command = xstrdup(src->command);
+}
+
+static const char *token_from_item(struct trailer_item *item)
+{
+   if (item->conf.key)
+   return item->conf.key;
+
+   return item->conf.name;
+}
+
+static struct trailer_item *new_trailer_item(struct trailer_item *conf_item,
+char *tok, char *val)
+{
+   struct trailer_item *new = xcalloc(sizeof(*new), 1);
+   new->value = val;
+
+   if (conf_item) {
+   duplicate_conf(&new->conf, &conf_item->conf);
+   new->token = xstrdup(token_from_item(conf_item));
+   free(tok);
+   } else
+   new->token = tok;
+
+   return new;
+}
+
+static int token_matches_item(const char *tok, struct trailer_item *item, int 
alnum_len)
+{
+   if (!strncasecmp(tok, item->conf.name, alnum_len))
+   return 1;
+   return item->conf.key ? !strncasecmp(tok, item->conf.key, alnum_len) : 
0;
+}
+
+static struct trailer_item *create_trailer_item(const char *string)
+{
+   struct strbuf tok = STRBUF_INIT;
+   struct strbuf val = STRBUF_INIT;
+   struct trailer_item *item;
+   int tok_alnum_len;
+
+   if (parse_trailer(&tok, &val, string))
+   return NULL;
+
+   tok_alnum_len = alnum_len(tok.buf, tok.len);
+
+   /* Lookup if the token matches something in the config */
+   for (item = first_conf_item; item; item = item->next) {
+   if (token_matches_item(tok.buf, item, tok_alnum_len)) {
+   strbuf_release(&tok);
+   return new_trailer_item(item,
+   NULL,
+   strbuf_detach(&val, NULL));
+   }
+   }
+
+   return new_trailer_item(NULL,
+   strbuf_detach(&tok, NULL),
+   strbuf_detach(&val, NULL));
+}
+
+static void add_trailer_item(struct trailer_item **first,
+struct trailer_item **last,
+struct trailer_item *new)
+{
+   if (!new)
+   return;
+   if (!*last) {
+   *first = new;
+   *last = new;
+   } else {
+   (*last)->next = new;
+   new->previous = *last;
+   *last = new;
+   }
+}
+
+static struct trailer_item *process_command_line_args(struct string_list 
*trailers)
+{
+   struct trailer_item *arg_tok_first = NULL;
+   struct trailer_item *arg_tok_last = NULL;
+   struct string_list_item *tr;
+
+   for_each_string_list_item(tr, trailers) {
+   struct trailer_item *new = create_trailer_item(tr->string);
+   add_trailer_item(&arg_tok_first, &arg_tok_last, new);
+   }
+
+   return arg_tok_first;
+}
-- 
1.9.rc0.17.g651113e


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v12 06/11] trailer: put all the processing together and print

2014-05-24 Thread Christian Couder
This patch adds the process_trailers() function that
calls all the previously added processing functions
and then prints the results on the standard output.

Signed-off-by: Christian Couder 
Signed-off-by: Junio C Hamano 
---
 trailer.c | 56 
 trailer.h |  6 ++
 2 files changed, 62 insertions(+)
 create mode 100644 trailer.h

diff --git a/trailer.c b/trailer.c
index 40ad1a1..d648939 100644
--- a/trailer.c
+++ b/trailer.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "string-list.h"
+#include "trailer.h"
 /*
  * Copyright (c) 2013, 2014 Christian Couder 
  */
@@ -69,6 +70,24 @@ static void free_trailer_item(struct trailer_item *item)
free(item);
 }
 
+static void print_tok_val(const char *tok, const char *val)
+{
+   char c = tok[strlen(tok) - 1];
+   if (isalnum(c))
+   printf("%s: %s\n", tok, val);
+   else
+   printf("%s%s\n", tok, val);
+}
+
+static void print_all(struct trailer_item *first, int trim_empty)
+{
+   struct trailer_item *item;
+   for (item = first; item; item = item->next) {
+   if (!trim_empty || strlen(item->value) > 0)
+   print_tok_val(item->token, item->value);
+   }
+}
+
 static void add_arg_to_input_list(struct trailer_item *in_tok,
  struct trailer_item *arg_tok)
 {
@@ -632,3 +651,40 @@ static int process_input_file(struct strbuf **lines,
 
return patch_start;
 }
+
+static void free_all(struct trailer_item **first)
+{
+   while (*first) {
+   struct trailer_item *item = remove_first(first);
+   free_trailer_item(item);
+   }
+}
+
+void process_trailers(const char *file, int trim_empty, struct string_list 
*trailers)
+{
+   struct trailer_item *in_tok_first = NULL;
+   struct trailer_item *in_tok_last = NULL;
+   struct trailer_item *arg_tok_first;
+   struct strbuf **lines;
+   int patch_start;
+
+   git_config(git_trailer_config, NULL);
+
+   lines = read_input_file(file);
+
+   /* Print the lines before the trailers */
+   patch_start = process_input_file(lines, &in_tok_first, &in_tok_last);
+
+   arg_tok_first = process_command_line_args(trailers);
+
+   process_trailers_lists(&in_tok_first, &in_tok_last, &arg_tok_first);
+
+   print_all(in_tok_first, trim_empty);
+
+   free_all(&in_tok_first);
+
+   /* Print the lines after the trailers as is */
+   print_lines(lines, patch_start, INT_MAX);
+
+   strbuf_list_free(lines);
+}
diff --git a/trailer.h b/trailer.h
new file mode 100644
index 000..8eb25d5
--- /dev/null
+++ b/trailer.h
@@ -0,0 +1,6 @@
+#ifndef TRAILER_H
+#define TRAILER_H
+
+void process_trailers(const char *file, int trim_empty, struct string_list 
*trailers);
+
+#endif /* TRAILER_H */
-- 
1.9.rc0.17.g651113e


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v2] commit: support commit.verbose and --no-verbose

2014-05-24 Thread Caleb Thompson
Incorporated changes from Duy Nguyen and Jeremiah Mahler.

Jeremiah, I didn't make the changes about `<<-EOF` or `test_expect_success`
because I'm guessing that keeping the local style of the code intact is more
important than using those. Do you think it makes sense to refactor the rest of
the test file (t/t7507-commit-verbose.sh) to use those? I could also change the
other `git config` calls to use `test_config`.

Duy, you were right about `-V`. Do you know of a simple way to add that
shortened flag? `OPT_BOOL('v', "verbose", ...)` gives me `-v`, `--verbose`, and
`--no-verbose`, but no `-V` as a shortened form of `--no-verbose`.

commit 1a49356b87c9028e68e731f34790c11a3075f736
Author: Caleb Thompson 
Date:   Fri May 23 11:47:44 2014 -0500

commit: support commit.verbose and --no-verbose

Add a new configuration variable commit.verbose to implicitly pass
`--verbose` to `git-commit`. Add `--no-verbose` to commit to negate that
setting.

Signed-off-by: Caleb Thompson 
Reviewed-by: Duy Nguyen 
Reviewed-by: Jeremiah Mahler 

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 1932e9b..a245928 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1009,6 +1009,11 @@ commit.template::
"`~/`" is expanded to the value of `$HOME` and "`~user/`" to the
specified user's home directory.

+commit.verbose::
+   A boolean to enable/disable inclusion of diff information in the
+   commit message template when using an editor to prepare the commit
+   message.  Defaults to false.
+
 credential.helper::
Specify an external helper to be called when a username or
password credential is needed; the helper may consult external
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 0bbc8f5..d7b50e2 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -282,7 +282,13 @@ configuration variable documented in linkgit:git-config[1].
Show unified diff between the HEAD commit and what
would be committed at the bottom of the commit message
template.  Note that this diff output doesn't have its
-   lines prefixed with '#'.
+   lines prefixed with '#'.  The `commit.verbose` configuration
+   variable can be set to true to implicitly send this option.
+
+--no-verbose::
+   Do not show the unified diff  at the bottom of the commit message
+   template.  This is the default behavior, but can be used to override
+   the`commit.verbose` configuration variable.

 -q::
 --quiet::
diff --git a/builtin/commit.c b/builtin/commit.c
index 9cfef6c..7978d7f 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1417,6 +1417,10 @@ static int git_commit_config(const char *k, const char 
*v, void *cb)
sign_commit = git_config_bool(k, v) ? "" : NULL;
return 0;
}
+   if (!strcmp(k, "commit.verbose")) {
+   verbose = git_config_bool(k, v);
+   return 0;
+   }

status = git_gpg_config(k, v, NULL);
if (status)
@@ -1484,7 +1488,7 @@ int cmd_commit(int argc, const char **argv, const char 
*prefix)
static struct wt_status s;
static struct option builtin_commit_options[] = {
OPT__QUIET(&quiet, N_("suppress summary after successful 
commit")),
-   OPT__VERBOSE(&verbose, N_("show diff in commit message 
template")),
+   OPT_BOOL('v', "verbose", &verbose, N_("show diff in commit 
message template")),
 
OPT_GROUP(N_("Commit message options")),
OPT_FILENAME('F', "file", &logfile, N_("read message from 
file")),
diff --git a/contrib/completion/git-completion.bash 
b/contrib/completion/git-completion.bash
index 2c59a76..b8f4b94 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1976,6 +1976,7 @@ _git_config ()
color.ui
commit.status
commit.template
+   commit.verbose
core.abbrev
core.askpass
core.attributesfile
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index 2ddf28c..bea5d88 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -10,6 +10,12 @@ EOF
 chmod +x check-for-diff
 test_set_editor "$PWD/check-for-diff"
 
+cat >check-for-no-diff file &&
+   git add file &&
+   test_config commit.verbose true &&
+   check_message message
+'
+
+test_expect_success 'commit does not show verbose diff with --no-verbose' '
+   echo morecontent >file &&
+   git add file &&
+   test_config commit.verbose true &&
+   test_set_editor "$PW