[RFC v2] blame: new option --prefer-first to better handle merged cherry-picks

2014-01-12 Thread Bernhard R. Link
Allows to disable the git blame optimization of assuming that if there is a
parent of a merge commit that has the exactly same file content, then
only this parent is to be looked at.

This optimization, while being faster in the usual case, means that in
the case of cherry-picks the blamed commit depends on which other commits
touched a file.

If for example one commit A modified both files b and c. And there are
commits B and C, B only modifies file b and C only modifies file c
(so that no conflicts happen), and assume A is cherry-picked as A'
and the two branches then merged:

--o-B---A
   \ \
---C---A'--M---

Then without this new option git blame blames the A|A' changes of
file b to A while blaming the changes of c to A'.
With the new option --prefer-first it blames both changes to the
same commit and to the one more on the "left" side of the graph.

Signed-off-by: Bernhard R. Link 
---
 Documentation/blame-options.txt | 6 ++
 builtin/blame.c | 7 +--
 2 files changed, 11 insertions(+), 2 deletions(-)

 Differences to first round: rename option and describe the effect
 instead of the implementation in documentation.

diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index 0cebc4f..b2e7fb8 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -48,6 +48,12 @@ include::line-range-format.txt[]
Show the result incrementally in a format designed for
machine consumption.
 
+--prefer-first::
+   If a line was introduced by two commits (for example via
+   a merged cherry-pick), prefer the commit that was
+   first merged in the history of always following the
+   first parent.
+
 --encoding=::
Specifies the encoding used to output author names
and commit summaries. Setting it to `none` makes blame
diff --git a/builtin/blame.c b/builtin/blame.c
index 4916eb2..8ea34cf 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -45,6 +45,7 @@ static int incremental;
 static int xdl_opts;
 static int abbrev = -1;
 static int no_whole_file_rename;
+static int prefer_first;
 
 static enum date_mode blame_date_mode = DATE_ISO8601;
 static size_t blame_date_width;
@@ -1248,7 +1249,8 @@ static void pass_blame(struct scoreboard *sb, struct 
origin *origin, int opt)
porigin = find(sb, p, origin);
if (!porigin)
continue;
-   if (!hashcmp(porigin->blob_sha1, origin->blob_sha1)) {
+   if (!prefer_first &&
+   !hashcmp(porigin->blob_sha1, origin->blob_sha1)) {
pass_whole_blame(sb, origin, porigin);
origin_decref(porigin);
goto finish;
@@ -2247,7 +2249,8 @@ int cmd_blame(int argc, const char **argv, const char 
*prefix)
static const char *contents_from = NULL;
static const struct option options[] = {
OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries 
as we find them, incrementally")),
-   OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for 
boundary commits (Default: off)")),
+   OPT_BOOL(0, "prefer-first", &prefer_first, N_("Prefer blaming 
commits merged earlier")),
+   OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for 
boundary commits (Default: ff)")),
OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits 
as boundaries (Default: off)")),
OPT_BOOL(0, "show-stats", &show_stats, N_("Show work cost 
statistics")),
OPT_BIT(0, "score-debug", &output_option, N_("Show output score 
for blame entries"), OUTPUT_SHOW_SCORE),
-- 
1.8.5.1

Bernhard R. Link
-- 
F8AC 04D5 0B9B 064B 3383  C3DA AFFC 96D1 151D FFDC
--
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


Fwd: Add a bugzilla website

2014-01-12 Thread Stefan Saasen
> In any case, adding value to the existing process is hard (because it
> works quite well!) and probably requires significantly more work to
> even understand what that value might look like. This, I think, is the
> key reason it is hard to truly get started with any bug tracking
> solution; the solution is not obvious, and the current (very
> customised) workflow is not supported directly by any tool.
>
> Regards,
>
> Andrew Ardill
>
> [1] https://git-scm.atlassian.net


I think you summarised the challenges very well and I don’t think there is an
obvious answer to that.

I’d just like to offer any help that I or we (Atlassian) could give you. Given
your experience with JIRA I’m sure that you’ve got everything covered, but if
you need anything, please ping me.

Re-reading the old discussions there was a concern that the issue data was not
available, I just wanted to chime in and mention that if you are using a JIRA
OnDemand (e.g. the https://git-scm.atlassian.net) instance you can get the full
backup of your data (including any attachments, data available as XML) and
there is a JIRA REST API as well.

I know that this is just a very tangential concern given the challenges of
making an issue tracker work with an email based workflow that has proven quite
successful but I at least wanted to address that and offer any help you might
need.


Cheers,
Stefan
--
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] Make 'git help everyday' work

2014-01-12 Thread Philip Oakley
The "Everyday GIT With 20 Commands Or So" is not accessible via the
Git help system. Fix that.

Move everyday.txt to giteveryday.txt.
Update giteveryday.txt to fit man page formatting.
Add standard man page section titles.
Also adjust anchor text markup for man page format.
Add giteveryday to the manpages make list.
Deprecate the old everyday.txt as *.txto in the same manner
as bd4a3d61 (Rename {git- => git}remote-helpers.txt,
2013-01-31), including a link to the new man page.

Add 'everyday' to the help --guides list.
Update git(1) and 5 other links to giteveryday.

Signed-off-by: Philip Oakley 
---

The series has been squashed into a single complete patch as
suggested by Jonathan Nieder, and now uses the same deprecation
techniques as the old remote-helpers as pointed out by Junio.

The diff is now smaller because of the shift to '.txto' for the
obsolete old file, so there is no longer a bulk deletion listed.

---
 Documentation/Makefile  |  3 +-
 Documentation/everyday.txto |  9 ++
 Documentation/git.txt   |  4 +--
 Documentation/gitcore-tutorial.txt  |  2 +-
 Documentation/gitcvs-migration.txt  |  2 +-
 Documentation/{everyday.txt => giteveryday.txt} | 39 +
 Documentation/gitglossary.txt   |  2 +-
 Documentation/gittutorial-2.txt |  4 +--
 Documentation/gittutorial.txt   |  4 +--
 README  |  2 +-
 builtin/help.c  |  1 +
 11 files changed, 49 insertions(+), 23 deletions(-)
 create mode 100644 Documentation/everyday.txto
 rename Documentation/{everyday.txt => giteveryday.txt} (94%)

diff --git a/Documentation/Makefile b/Documentation/Makefile
index 91a12c7..861560b 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -23,6 +23,7 @@ MAN7_TXT += gitcore-tutorial.txt
 MAN7_TXT += gitcredentials.txt
 MAN7_TXT += gitcvs-migration.txt
 MAN7_TXT += gitdiffcore.txt
+MAN7_TXT += giteveryday.txt
 MAN7_TXT += gitglossary.txt
 MAN7_TXT += gitnamespaces.txt
 MAN7_TXT += gitrevisions.txt
@@ -35,10 +36,10 @@ MAN_XML = $(patsubst %.txt,%.xml,$(MAN_TXT))
 MAN_HTML = $(patsubst %.txt,%.html,$(MAN_TXT))
 
 OBSOLETE_HTML = git-remote-helpers.html
+OBSOLETE_HTML += everyday.html
 DOC_HTML = $(MAN_HTML) $(OBSOLETE_HTML)
 
 ARTICLES = howto-index
-ARTICLES += everyday
 ARTICLES += git-tools
 ARTICLES += git-bisect-lk2009
 # with their own formatting rules.
diff --git a/Documentation/everyday.txto b/Documentation/everyday.txto
new file mode 100644
index 000..3f8a4ea
--- /dev/null
+++ b/Documentation/everyday.txto
@@ -0,0 +1,9 @@
+Everyday Git With 20 Commands Or So
+===
+
+This document has been moved to linkgit:giteveryday[1].
+
+Please let the owners of the referring site know so that they can update the
+link you clicked to get here.
+
+Thanks.
\ No newline at end of file
diff --git a/Documentation/git.txt b/Documentation/git.txt
index aec3726..bf06dcc 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -22,7 +22,7 @@ unusually rich command set that provides both high-level 
operations
 and full access to internals.
 
 See linkgit:gittutorial[7] to get started, then see
-link:everyday.html[Everyday Git] for a useful minimum set of
+linkgit:giteveryday[7] for a useful minimum set of
 commands.  The link:user-manual.html[Git User's Manual] has a more
 in-depth introduction.
 
@@ -1037,7 +1037,7 @@ subscribed to the list to send a message there.
 SEE ALSO
 
 linkgit:gittutorial[7], linkgit:gittutorial-2[7],
-link:everyday.html[Everyday Git], linkgit:gitcvs-migration[7],
+linkgit:giteveryday[7], linkgit:gitcvs-migration[7],
 linkgit:gitglossary[7], linkgit:gitcore-tutorial[7],
 linkgit:gitcli[7], link:user-manual.html[The Git User's Manual],
 linkgit:gitworkflows[7]
diff --git a/Documentation/gitcore-tutorial.txt 
b/Documentation/gitcore-tutorial.txt
index 058a352..ccf31dd 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -1667,7 +1667,7 @@ linkgit:gittutorial[7],
 linkgit:gittutorial-2[7],
 linkgit:gitcvs-migration[7],
 linkgit:git-help[1],
-link:everyday.html[Everyday git],
+linkgit:giteveryday[7],
 link:user-manual.html[The Git User's Manual]
 
 GIT
diff --git a/Documentation/gitcvs-migration.txt 
b/Documentation/gitcvs-migration.txt
index 5ea94cb..da8688d 100644
--- a/Documentation/gitcvs-migration.txt
+++ b/Documentation/gitcvs-migration.txt
@@ -194,7 +194,7 @@ linkgit:gittutorial[7],
 linkgit:gittutorial-2[7],
 linkgit:gitcore-tutorial[7],
 linkgit:gitglossary[7],
-link:everyday.html[Everyday Git],
+linkgit:giteveryday[7],
 link:user-manual.html[The Git User's Manual]
 
 GIT
diff --git a/Documentation/everyday.txt b/Documentation/giteveryday.txt
similarity index 94%
rename from Documentation/everyday.txt
rename to Documentation/giteveryday.txt
index 2a18c1f..2939458 100644

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

2014-01-12 Thread Pete Wyckoff
frrr...@gmail.com wrote on Fri, 10 Jan 2014 18:18 +:
> 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.

Thanks for the patch, but I'm curious how you'd like this to
work.  I never use the option myself.

As it is, --prepare-p4-only generates a file in /tmp/ that has
exactly the contents you'd see in the editor during "git p4
submit".  It includes the diff of the change, presumably to help
with writing the description.

Now you can't actually feed this file directly to "p4 submit"
without deleting the diff.  That's the part you don't like?

-- 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


[PATCH 2/3] interpret_branch_name: factor out upstream handling

2014-01-12 Thread Ramkumar Ramachandra
From: Jeff King 

This function checks a few different @{}-constructs. The
early part checks for and dispatches us to helpers for each
construct, but the code for handling @{upstream} is inline.

Let's factor this out into its own function. This makes
interpret_branch_name more readable, and will make it much
simpler to add more constructs in future patches.

While we're at it, let's also break apart the refactored
code into a few helper functions. These will be useful when
we implement similar @{upstream}-like constructs.

Signed-off-by: Jeff King 
Signed-off-by: Ramkumar Ramachandra 
---
 sha1_name.c | 83 ++---
 1 file changed, 52 insertions(+), 31 deletions(-)

diff --git a/sha1_name.c b/sha1_name.c
index b1873d8..7ebb8ee 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -1046,6 +1046,54 @@ static int reinterpret(const char *name, int namelen, 
int len, struct strbuf *bu
return ret - used + len;
 }
 
+static void set_shortened_ref(struct strbuf *buf, const char *ref)
+{
+   char *s = shorten_unambiguous_ref(ref, 0);
+   strbuf_reset(buf);
+   strbuf_addstr(buf, s);
+   free(s);
+}
+
+static const char *get_upstream_branch(const char *branch_buf, int len)
+{
+   char *branch = xstrndup(branch_buf, len);
+   struct branch *upstream = branch_get(*branch ? branch : NULL);
+
+   /*
+* Upstream can be NULL only if branch refers to HEAD and HEAD
+* points to something different than a branch.
+*/
+   if (!upstream)
+   die(_("HEAD does not point to a branch"));
+   if (!upstream->merge || !upstream->merge[0]->dst) {
+   if (!ref_exists(upstream->refname))
+   die(_("No such branch: '%s'"), branch);
+   if (!upstream->merge) {
+   die(_("No upstream configured for branch '%s'"),
+   upstream->name);
+   }
+   die(
+   _("Upstream branch '%s' not stored as a remote-tracking 
branch"),
+   upstream->merge[0]->src);
+   }
+   free(branch);
+
+   return upstream->merge[0]->dst;
+}
+
+static int interpret_upstream_mark(const char *name, int namelen,
+  int at, struct strbuf *buf)
+{
+   int len;
+
+   len = upstream_mark(name + at, namelen - at);
+   if (!len)
+   return -1;
+
+   set_shortened_ref(buf, get_upstream_branch(name, at));
+   return len + at;
+}
+
 /*
  * This reads short-hand syntax that not only evaluates to a commit
  * object name, but also can act as if the end user spelled the name
@@ -1070,9 +1118,7 @@ static int reinterpret(const char *name, int namelen, int 
len, struct strbuf *bu
 int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
 {
char *cp;
-   struct branch *upstream;
int len = interpret_nth_prior_checkout(name, buf);
-   int tmp_len;
 
if (!namelen)
namelen = strlen(name);
@@ -1094,36 +1140,11 @@ int interpret_branch_name(const char *name, int 
namelen, struct strbuf *buf)
if (len > 0)
return reinterpret(name, namelen, len, buf);
 
-   tmp_len = upstream_mark(cp, namelen - (cp - name));
-   if (!tmp_len)
-   return -1;
+   len = interpret_upstream_mark(name, namelen, cp - name, buf);
+   if (len > 0)
+   return len;
 
-   len = cp + tmp_len - name;
-   cp = xstrndup(name, cp - name);
-   upstream = branch_get(*cp ? cp : NULL);
-   /*
-* Upstream can be NULL only if cp refers to HEAD and HEAD
-* points to something different than a branch.
-*/
-   if (!upstream)
-   die(_("HEAD does not point to a branch"));
-   if (!upstream->merge || !upstream->merge[0]->dst) {
-   if (!ref_exists(upstream->refname))
-   die(_("No such branch: '%s'"), cp);
-   if (!upstream->merge) {
-   die(_("No upstream configured for branch '%s'"),
-   upstream->name);
-   }
-   die(
-   _("Upstream branch '%s' not stored as a remote-tracking 
branch"),
-   upstream->merge[0]->src);
-   }
-   free(cp);
-   cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
-   strbuf_reset(buf);
-   strbuf_addstr(buf, cp);
-   free(cp);
-   return len;
+   return -1;
 }
 
 int strbuf_branchname(struct strbuf *sb, const char *name)
-- 
1.8.5.2.313.g5abf4c0.dirty

--
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 3/3] remote: introduce and fill branch->pushremote

2014-01-12 Thread Ramkumar Ramachandra
When a caller uses branch_get() to retrieve a "struct branch", they get
the per-branch remote name and a pointer to the remote struct. However,
they have no way of knowing about the per-branch pushremote from this
interface. So, let's expose that information via fields similar to
"remote" and "remote_name"; "pushremote" and "pushremote_name".

Helped-by: Jeff King 
Signed-off-by: Ramkumar Ramachandra 
---
 remote.c | 15 ---
 remote.h |  3 +++
 2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/remote.c b/remote.c
index a89efab..286cdce 100644
--- a/remote.c
+++ b/remote.c
@@ -351,9 +351,10 @@ static int handle_config(const char *key, const char 
*value, void *cb)
explicit_default_remote_name = 1;
}
} else if (!strcmp(subkey, ".pushremote")) {
+   if (git_config_string(&branch->pushremote_name, key, 
value))
+   return -1;
if (branch == current_branch)
-   if (git_config_string(&pushremote_name, key, 
value))
-   return -1;
+   pushremote_name = branch->pushremote_name;
} else if (!strcmp(subkey, ".merge")) {
if (!value)
return config_error_nonbool(key);
@@ -1543,7 +1544,9 @@ struct branch *branch_get(const char *name)
ret = current_branch;
else
ret = make_branch(name, 0);
-   if (ret && ret->remote_name) {
+   if (!ret)
+   return ret;
+   if (ret->remote_name) {
ret->remote = remote_get(ret->remote_name);
if (ret->merge_nr) {
int i;
@@ -1557,6 +1560,12 @@ struct branch *branch_get(const char *name)
}
}
}
+   if (ret->pushremote_name)
+   ret->pushremote = remote_get(ret->pushremote_name);
+   else if (pushremote_name)
+   ret->pushremote = remote_get(pushremote_name);
+   else
+   ret->pushremote = ret->remote;
return ret;
 }
 
diff --git a/remote.h b/remote.h
index 00c6a76..ac5aadc 100644
--- a/remote.h
+++ b/remote.h
@@ -201,6 +201,9 @@ struct branch {
const char *remote_name;
struct remote *remote;
 
+   const char *pushremote_name;
+   struct remote *pushremote;
+
const char **merge_name;
struct refspec **merge;
int merge_nr;
-- 
1.8.5.2.313.g5abf4c0.dirty

--
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 0/3] Minor preparation for @{publish}

2014-01-12 Thread Ramkumar Ramachandra
Hi,

I'm getting ready to switch timezones in a few days, so the @{publish}
series is on hold for some time. In the meantime, I thought I'd send
in a few patches early.

[1/3] is a minor typo fix I happened to notice while writing tests.

[2/3] is an is an excellent patch by Peff, that greatly helped
prettify the code. It's a pure refactor and should have no effect on
functionality.

[3/3] introduces branch->pushremote corresponding to branch->remote,
which is crucial for getting @{publish} from the branch_get()
interface. Peff had sent a similar patch, but mine has some subtle
differences.

Thanks.

Jeff King (1):
  interpret_branch_name: factor out upstream handling

Ramkumar Ramachandra (2):
  t1507 (rev-parse-upstream): fix typo in test title
  remote: introduce and fill branch->pushremote

 remote.c  | 15 ++--
 remote.h  |  3 ++
 sha1_name.c   | 83 +++
 t/t1507-rev-parse-upstream.sh |  2 +-
 4 files changed, 68 insertions(+), 35 deletions(-)

-- 
1.8.5.2.313.g5abf4c0.dirty

--
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 1/3] t1507 (rev-parse-upstream): fix typo in test title

2014-01-12 Thread Ramkumar Ramachandra
Signed-off-by: Ramkumar Ramachandra 
---
 t/t1507-rev-parse-upstream.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
index 2a19e79..15f2e7e 100755
--- a/t/t1507-rev-parse-upstream.sh
+++ b/t/t1507-rev-parse-upstream.sh
@@ -54,7 +54,7 @@ test_expect_success 'my-side@{upstream} resolves to correct 
full name' '
test refs/remotes/origin/side = "$(full_name my-side@{u})"
 '
 
-test_expect_success 'refs/heads/my-side@{upstream} does not resolve to 
my-side{upstream}' '
+test_expect_success 'refs/heads/my-side@{upstream} does not resolve to 
my-side@{upstream}' '
test_must_fail full_name refs/heads/my-side@{upstream}
 '
 
-- 
1.8.5.2.313.g5abf4c0.dirty

--
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: Synchronize git-credential-wincred from msysgit

2014-01-12 Thread Erik Faye-Lund
On Sun, Jan 12, 2014 at 12:59 PM, 乙酸鋰  wrote:
> Hi,
>
> Please cherry pick from msysgit/git
> commit 3c8cbb4edc8f577940c52115c992d17575587f99
>
> to synchronize git-credential-wincred
>
> This was the change they made half year ago.

It's actually a two-patch series.

Cover-letter:
http://permalink.gmane.org/gmane.comp.version-control.msysgit/18626

Patches:
http://article.gmane.org/gmane.comp.version-control.msysgit/18627
http://article.gmane.org/gmane.comp.version-control.msysgit/18628
--
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


Synchronize git-credential-wincred from msysgit

2014-01-12 Thread 乙酸鋰
Hi,

Please cherry pick from msysgit/git
commit 3c8cbb4edc8f577940c52115c992d17575587f99

to synchronize git-credential-wincred

This was the change they made half year ago.
--
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: Git: Question about specific subtree usage

2014-01-12 Thread THILLOSEN Andreas
Well... submodules didn't seem to fit the job, because it seems that the
sources of only one submodule at a time can be checked out.
And in my case, I would need to have the sources of all submodules
simultaneously.
I was talking about subtrees, because I thought it could be a better
tool for this job (and it had the reputation of being less cumbersome
than submodules).

Maybe such a job cannot be done with pure git commands.
Thanks for pointing me this "git-repo" script tool... the description
seems to fit my needs!
I will test it, and hope it will work on Windows (project is
unfortunately very Windows-oriented, for the time being).

Thanks for you help!

Andreas THILLOSEN.


Le 01/12/14 11:29, Torsten Bögershausen a écrit :
> On 2014-01-12 01.23, THILLOSEN Andreas wrote:
>> Hi,
>>
>> I have a question about a specific use case for subtrees, but I'm not
> I feel a little bit confused: Are you talking about git submodules?
>
> And you may want to have a look at the repo tool:
> https://code.google.com/p/git-repo/
>
> HTH
> /Torsten
>
>
>
>

--
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 4/6] read-cache: get "updated" path list from file watcher

2014-01-12 Thread Nguyễn Thái Ngọc Duy
A new command is added to file watcher to send back the list of
updated files to git. These entries will have CE_WATCHED removed. The
remaining CE_WATCHED entries will have CE_VALID set (i.e. no changes
and no lstat either).

The file watcher keeps reporting the same "updated" list until it
receives "forget" commands, which should only be issued after the
updated index is written down. This ensures that if git crashes half
way before it could update the index (or multiple processes is reading
the same index), "updated" info is not lost. After the index is
updated (e.g. in this case because of toggling CE_WATCHED bits), git
sends the new index signature to the file watcher.

The file watcher does not cache stat info and send back to git. Its
main purpose is to reduce lstat on most untouched files, not to
completely eliminate lstat.

One can see that, assuming CE_WATCHED is magically set in some
entries, they will be all cleared over the time and we need to do
lstat on all entries. We haven't talked about how CE_WATCHED is set
yet. More to come later.

TODO: get as many paths as possible in one packet to reduce round
trips

Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 cache.h|  1 +
 file-watcher.c | 35 +++---
 read-cache.c   | 91 +++---
 3 files changed, 119 insertions(+), 8 deletions(-)

diff --git a/cache.h b/cache.h
index 6a182b5..2eddc1e 100644
--- a/cache.h
+++ b/cache.h
@@ -282,6 +282,7 @@ struct index_state {
struct hash_table dir_hash;
unsigned char sha1[20];
int watcher;
+   struct string_list *updated_entries;
 };
 
 extern struct index_state the_index;
diff --git a/file-watcher.c b/file-watcher.c
index 66b44e5..6aeed4d 100644
--- a/file-watcher.c
+++ b/file-watcher.c
@@ -1,7 +1,16 @@
 #include "cache.h"
 #include "sigchain.h"
+#include "string-list.h"
 
 static char index_signature[41];
+static struct string_list updated = STRING_LIST_INIT_DUP;
+static int updated_sorted;
+
+static void reset(const char *sig)
+{
+   string_list_clear(&updated, 0);
+   strlcpy(index_signature, sig, sizeof(index_signature));
+}
 
 static int handle_command(int fd, char *msg, int msgsize)
 {
@@ -22,10 +31,28 @@ static int handle_command(int fd, char *msg, int msgsize)
sendtof(fd, 0, &sun, socklen, "hello %s", index_signature);
if (!strcmp(index_signature, arg))
return 0;
-   /*
-* Index SHA-1 mismatch, something has gone
-* wrong. Clean up and start over.
-*/
+   reset(arg);
+   } else if ((arg = skip_prefix(msg, "reset "))) {
+   reset(arg);
+   } else if (!strcmp(msg, "status")) {
+   int i;
+   for (i = 0; i < updated.nr; i++)
+   sendto(fd, updated.items[i].string,
+  strlen(updated.items[i].string),
+  0, &sun, socklen);
+   sendtof(fd, 0, &sun, socklen, "%c", 0);
+   } else if ((arg = skip_prefix(msg, "forget "))) {
+   struct string_list_item *item;
+   if (!updated_sorted) {
+   sort_string_list(&updated);
+   updated_sorted = 1;
+   }
+   item = string_list_lookup(&updated, arg);
+   if (item)
+   unsorted_string_list_delete_item(&updated,
+item - updated.items,
+0);
+   } else if ((arg = skip_prefix(msg, "bye "))) {
strlcpy(index_signature, arg, sizeof(index_signature));
} else {
die("unrecognized command %s", msg);
diff --git a/read-cache.c b/read-cache.c
index 506d488..caa2298 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1443,6 +1443,55 @@ static struct cache_entry *create_from_disk(struct 
ondisk_cache_entry *ondisk,
return ce;
 }
 
+static void update_watched_files(struct index_state *istate)
+{
+   int i;
+   if (istate->watcher == -1)
+   return;
+   if (writef(istate->watcher, "status") < 0)
+   goto failed;
+   for (;;) {
+   char line[1024];
+   int len;
+   len = read(istate->watcher, line, sizeof(line) - 1);
+   if (len <= 0)
+   goto failed;
+   line[len] = '\0';
+   if (len == 1 && line[0] == '\0')
+   break;
+   i = index_name_pos(istate, line, len);
+   if (i < 0)
+   continue;
+   if (istate->cache[i]->ce_flags & CE_WATCHED) {
+   istate->cache[i]->ce_flags &= ~CE_WATCHED;
+   istate->cache_changed = 1;
+   }
+   if (!istate->updated_entries) {
+   struct s

[PATCH 1/6] read-cache: save trailing sha-1

2014-01-12 Thread Nguyễn Thái Ngọc Duy
This will be used as signature to know if the index has changed.

Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 cache.h  | 1 +
 read-cache.c | 7 ---
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/cache.h b/cache.h
index ce377e1..7f7f306 100644
--- a/cache.h
+++ b/cache.h
@@ -279,6 +279,7 @@ struct index_state {
 initialized : 1;
struct hash_table name_hash;
struct hash_table dir_hash;
+   unsigned char sha1[20];
 };
 
 extern struct index_state the_index;
diff --git a/read-cache.c b/read-cache.c
index 33dd676..3b6daf1 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1269,10 +1269,11 @@ struct ondisk_cache_entry_extended {
ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
ondisk_cache_entry_size(ce_namelen(ce)))
 
-static int verify_hdr(struct cache_header *hdr, unsigned long size)
+static int verify_hdr(struct cache_header *hdr,
+ unsigned long size,
+ unsigned char *sha1)
 {
git_SHA_CTX c;
-   unsigned char sha1[20];
int hdr_version;
 
if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
@@ -1461,7 +1462,7 @@ int read_index_from(struct index_state *istate, const 
char *path)
close(fd);
 
hdr = mmap;
-   if (verify_hdr(hdr, mmap_size) < 0)
+   if (verify_hdr(hdr, mmap_size, istate->sha1) < 0)
goto unmap;
 
istate->version = ntohl(hdr->hdr_version);
-- 
1.8.5.2.240.g8478abd

--
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 2/6] read-cache: new extension to mark what file is watched

2014-01-12 Thread Nguyễn Thái Ngọc Duy
If an entry is "watched", git lets an external program decide if the
entry is modified or not. It's more like --assume-unchanged, but
designed to be controlled by machine.

We are running out of on-disk ce_flags, so instead of extending
on-disk entry format again, "watched" flags are in-core only and
stored as extension instead.

Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 cache.h  |  1 +
 read-cache.c | 41 -
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/cache.h b/cache.h
index 7f7f306..dfa8622 100644
--- a/cache.h
+++ b/cache.h
@@ -168,6 +168,7 @@ struct cache_entry {
 
 /* used to temporarily mark paths matched by pathspecs */
 #define CE_MATCHED   (1 << 26)
+#define CE_WATCHED   (1 << 27)
 
 /*
  * Extended on-disk flags
diff --git a/read-cache.c b/read-cache.c
index 3b6daf1..098d3b6 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -33,6 +33,7 @@ static struct cache_entry *refresh_cache_entry(struct 
cache_entry *ce, int reall
 #define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
 #define CACHE_EXT_TREE 0x54524545  /* "TREE" */
 #define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUC" */
+#define CACHE_EXT_WATCH 0x57415443   /* "WATC" */
 
 struct index_state the_index;
 
@@ -1289,6 +1290,19 @@ static int verify_hdr(struct cache_header *hdr,
return 0;
 }
 
+static void read_watch_extension(struct index_state *istate, uint8_t *data,
+unsigned long sz)
+{
+   int i;
+   if ((istate->cache_nr + 7) / 8 != sz) {
+   error("invalid 'WATC' extension");
+   return;
+   }
+   for (i = 0; i < istate->cache_nr; i++)
+   if (data[i / 8] & (1 << (i % 8)))
+   istate->cache[i]->ce_flags |= CE_WATCHED;
+}
+
 static int read_index_extension(struct index_state *istate,
const char *ext, void *data, unsigned long sz)
 {
@@ -1299,6 +1313,9 @@ static int read_index_extension(struct index_state 
*istate,
case CACHE_EXT_RESOLVE_UNDO:
istate->resolve_undo = resolve_undo_read(data, sz);
break;
+   case CACHE_EXT_WATCH:
+   read_watch_extension(istate, data, sz);
+   break;
default:
if (*ext < 'A' || 'Z' < *ext)
return error("index uses %.4s extension, which we do 
not understand",
@@ -1777,7 +1794,7 @@ int write_index(struct index_state *istate, int newfd)
 {
git_SHA_CTX c;
struct cache_header hdr;
-   int i, err, removed, extended, hdr_version;
+   int i, err, removed, extended, hdr_version, has_watches = 0;
struct cache_entry **cache = istate->cache;
int entries = istate->cache_nr;
struct stat st;
@@ -1786,6 +1803,8 @@ int write_index(struct index_state *istate, int newfd)
for (i = removed = extended = 0; i < entries; i++) {
if (cache[i]->ce_flags & CE_REMOVE)
removed++;
+   else if (cache[i]->ce_flags & CE_WATCHED)
+   has_watches++;
 
/* reduce extended entries if possible */
cache[i]->ce_flags &= ~CE_EXTENDED;
@@ -1857,6 +1876,26 @@ int write_index(struct index_state *istate, int newfd)
if (err)
return -1;
}
+   if (has_watches) {
+   int id, sz = (entries - removed + 7) / 8;
+   uint8_t *data = xmalloc(sz);
+   memset(data, 0, sz);
+   for (i = 0, id = 0; i < entries && has_watches; i++) {
+   struct cache_entry *ce = cache[i];
+   if (ce->ce_flags & CE_REMOVE)
+   continue;
+   if (ce->ce_flags & CE_WATCHED) {
+   data[id / 8] |= 1 << (id % 8);
+   has_watches--;
+   }
+   id++;
+   }
+   err = write_index_ext_header(&c, newfd, CACHE_EXT_WATCH, sz) < 0
+   || ce_write(&c, newfd, data, sz) < 0;
+   free(data);
+   if (err)
+   return -1;
+   }
 
if (ce_flush(&c, newfd) || fstat(newfd, &st))
return -1;
-- 
1.8.5.2.240.g8478abd

--
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 0/6] inotify support

2014-01-12 Thread Nguyễn Thái Ngọc Duy
It's been 37 weeks since Robert Zeh's attempt to bring inotify support
to Git [1] and unless I missed some mails, no updates since. So here's
another attempt with my preferred approach (can't help it, playing
with your own ideas is more fun than improving other people's code)

To compare to Robert's approach:

- This one uses UNIX datagram socket. If I read its man page right,
  unix socket respects the containing directory's permission. Which
  means on normal repos, only the user process can access. On shared
  repos, multiple users can access it. This should work on Mac.
  Windows will need a different transport.

- The daemon is dumb. It passes the paths around and that's it.
  lstat() is done by git. If I design it right, there's should not be
  any race conditions that make git miss file updates.

- CE_VALID is reused to avoid mass changes (granted there's other
  neat ways as well). I quite like the idea of machine-controlled
  CE_VALID.

inotify support has the potential of reducing syscalls in
read_directory() as well. I wrote about using lstat() to reduce
readdir() a while back, if that's implemented then inotify will fit in
nicely.

This is just a proof of concept. I'm sure I haven't handled all error
cases very well. The first five patches show the protocol and git
side's changes. The last one fills inotify in.

[1] http://thread.gmane.org/gmane.comp.version-control.git/215820/focus=78

Nguyễn Thái Ngọc Duy (6):
  read-cache: save trailing sha-1
  read-cache: new extension to mark what file is watched
  read-cache: connect to file watcher
  read-cache: get "updated" path list from file watcher
  read-cache: ask file watcher to watch files
  file-watcher: support inotify

 .gitignore   |   1 +
 Makefile |   1 +
 cache.h  |   4 +
 config.mak.uname |   1 +
 file-watcher.c (new) | 329 +++
 git-compat-util.h|   5 +
 pkt-line.c   |   2 +-
 pkt-line.h   |   2 +
 read-cache.c | 280 ++-
 wrapper.c|  27 +
 10 files changed, 645 insertions(+), 7 deletions(-)
 create mode 100644 file-watcher.c

-- 
1.8.5.2.240.g8478abd

--
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 5/6] read-cache: ask file watcher to watch files

2014-01-12 Thread Nguyễn Thái Ngọc Duy
We want to watch files that are never changed because lstat() on those
files is a wasted effort. So we sort unwatched files by date and start
adding them to the file watcher until it barfs (e.g. hits inotify
limit). Recently updated entries are also excluded from watch list.
CE_VALID is used in combination with CE_WATCHED. Those entries that
have CE_VALID already set will never be watched.

We send as many paths as possible in one packet in pkt-line
format. For small projects like git, all entries can be packed in one
packet. For large projects like webkit (182k entries) it takes two
packets. We may do prefix compression as well to send more in fewer
packets..

The file watcher replies how many entries it can watch (because at
least inotify has system limits).

Note that we still do lstat() on these new watched files because they
could have changed before the file watcher could watch them. Watched
files may only skip lstat() at the next git run.

Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 file-watcher.c | 27 
 pkt-line.c |  2 +-
 pkt-line.h |  2 ++
 read-cache.c   | 97 ++
 4 files changed, 127 insertions(+), 1 deletion(-)

diff --git a/file-watcher.c b/file-watcher.c
index 6aeed4d..35781fa 100644
--- a/file-watcher.c
+++ b/file-watcher.c
@@ -1,17 +1,41 @@
 #include "cache.h"
 #include "sigchain.h"
 #include "string-list.h"
+#include "pkt-line.h"
 
 static char index_signature[41];
 static struct string_list updated = STRING_LIST_INIT_DUP;
 static int updated_sorted;
 
+static int watch_path(char *path)
+{
+   return -1;
+}
+
 static void reset(const char *sig)
 {
string_list_clear(&updated, 0);
strlcpy(index_signature, sig, sizeof(index_signature));
 }
 
+static void watch_paths(char *buf, int maxlen,
+   int fd, struct sockaddr *sock,
+   socklen_t socklen)
+{
+   char *end = buf + maxlen;
+   int n, ret, len;
+   for (n = ret = 0; buf < end && !ret; buf += len) {
+   char ch;
+   len = packet_length(buf);
+   ch = buf[len];
+   buf[len] = '\0';
+   if (!(ret = watch_path(buf + 4)))
+   n++;
+   buf[len] = ch;
+   }
+   sendtof(fd, 0, sock, socklen, "fine %d", n);
+}
+
 static int handle_command(int fd, char *msg, int msgsize)
 {
struct sockaddr_un sun;
@@ -41,6 +65,9 @@ static int handle_command(int fd, char *msg, int msgsize)
   strlen(updated.items[i].string),
   0, &sun, socklen);
sendtof(fd, 0, &sun, socklen, "%c", 0);
+   } else if (starts_with(msg, "watch ")) {
+   watch_paths(msg + 6, len - 6,
+   fd, (struct sockaddr *)&sun, socklen);
} else if ((arg = skip_prefix(msg, "forget "))) {
struct string_list_item *item;
if (!updated_sorted) {
diff --git a/pkt-line.c b/pkt-line.c
index bc63b3b..b5af84e 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -135,7 +135,7 @@ static int get_packet_data(int fd, char **src_buf, size_t 
*src_size,
return ret;
 }
 
-static int packet_length(const char *linelen)
+int packet_length(const char *linelen)
 {
int n;
int len = 0;
diff --git a/pkt-line.h b/pkt-line.h
index 0a838d1..40470b9 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -75,6 +75,8 @@ char *packet_read_line(int fd, int *size);
  */
 char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size);
 
+int packet_length(const char *linelen);
+
 #define DEFAULT_PACKET_MAX 1000
 #define LARGE_PACKET_MAX 65520
 extern char packet_buffer[LARGE_PACKET_MAX];
diff --git a/read-cache.c b/read-cache.c
index caa2298..839fd7c 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -14,6 +14,7 @@
 #include "resolve-undo.h"
 #include "strbuf.h"
 #include "varint.h"
+#include "pkt-line.h"
 
 static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int 
really);
 
@@ -1537,6 +1538,90 @@ static void connect_watcher(struct index_state *istate, 
const char *path)
}
 }
 
+static int sort_by_date(const void *a_, const void *b_)
+{
+   const struct cache_entry *a = *(const struct cache_entry **)a_;
+   const struct cache_entry *b = *(const struct cache_entry **)b_;
+   uint32_t seca = a->ce_stat_data.sd_mtime.sec;
+   uint32_t secb = b->ce_stat_data.sd_mtime.sec;
+   return seca - secb;
+}
+
+static inline int ce_watchable(struct cache_entry *ce, time_t now)
+{
+   return !(ce->ce_flags & CE_WATCHED) &&
+   !(ce->ce_flags & CE_VALID) &&
+   (ce->ce_stat_data.sd_mtime.sec + 1800 < now);
+}
+
+static int do_watch_entries(struct index_state *istate,
+   struct cache_entry **cache,
+   struct strbuf *sb, int start, int now)
+{
+   char line[1024];
+   int i, len;
+
+   write(istate-

[PATCH 3/6] read-cache: connect to file watcher

2014-01-12 Thread Nguyễn Thái Ngọc Duy
This patch establishes a connection between a new file watcher daemon
and git. Each index file may have at most one file watcher attached to
it. The file watcher maintains a UNIX socket at
$GIT_DIR/index.watcher. Any process that has write access to $GIT_DIR
can talk to the file watcher.

A validation is performed after git connects to the file watcher to
make sure both sides have the same view. This is done by exchanging
the index signature (*) The file watcher keeps a copy of the signature
locally while git computes the signature from the index. If the
signatures do not match, something has gone wrong so both sides
reinitialize wrt. to file watching: the file watcher clears all
watches while git clears CE_WATCHED flags.

If the signatures match, we can trust the file watcher and git can
start asking questions that are not important to this patch.

TODO: do not let git hang if the file watcher refuses to
answer. Timeout and move on without file watcher support after 20ms or
so.

(*) for current index versions, the signature is the index SHA-1
trailer. But it could be something else (e.g. v5 does not have SHA-1
trailer)

Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 .gitignore   |   1 +
 Makefile |   1 +
 cache.h  |   1 +
 file-watcher.c (new) | 136 +++
 git-compat-util.h|   5 ++
 read-cache.c |  48 ++
 wrapper.c|  27 ++
 7 files changed, 219 insertions(+)
 create mode 100644 file-watcher.c

diff --git a/.gitignore b/.gitignore
index b5f9def..12c78f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,6 +56,7 @@
 /git-fast-import
 /git-fetch
 /git-fetch-pack
+/git-file-watcher
 /git-filter-branch
 /git-fmt-merge-msg
 /git-for-each-ref
diff --git a/Makefile b/Makefile
index b4af1e2..ca5dc96 100644
--- a/Makefile
+++ b/Makefile
@@ -536,6 +536,7 @@ PROGRAMS += $(EXTRA_PROGRAMS)
 PROGRAM_OBJS += credential-store.o
 PROGRAM_OBJS += daemon.o
 PROGRAM_OBJS += fast-import.o
+PROGRAM_OBJS += file-watcher.o
 PROGRAM_OBJS += http-backend.o
 PROGRAM_OBJS += imap-send.o
 PROGRAM_OBJS += sh-i18n--envsubst.o
diff --git a/cache.h b/cache.h
index dfa8622..6a182b5 100644
--- a/cache.h
+++ b/cache.h
@@ -281,6 +281,7 @@ struct index_state {
struct hash_table name_hash;
struct hash_table dir_hash;
unsigned char sha1[20];
+   int watcher;
 };
 
 extern struct index_state the_index;
diff --git a/file-watcher.c b/file-watcher.c
new file mode 100644
index 000..66b44e5
--- /dev/null
+++ b/file-watcher.c
@@ -0,0 +1,136 @@
+#include "cache.h"
+#include "sigchain.h"
+
+static char index_signature[41];
+
+static int handle_command(int fd, char *msg, int msgsize)
+{
+   struct sockaddr_un sun;
+   int len;
+   socklen_t socklen;
+   const char *arg;
+
+   socklen = sizeof(sun);
+   len = recvfrom(fd, msg, msgsize, 0, &sun, &socklen);
+   if (!len)
+   return -1;
+   if (len == -1)
+   die_errno("read");
+   msg[len] = '\0';
+
+   if ((arg = skip_prefix(msg, "hello "))) {
+   sendtof(fd, 0, &sun, socklen, "hello %s", index_signature);
+   if (!strcmp(index_signature, arg))
+   return 0;
+   /*
+* Index SHA-1 mismatch, something has gone
+* wrong. Clean up and start over.
+*/
+   strlcpy(index_signature, arg, sizeof(index_signature));
+   } else {
+   die("unrecognized command %s", msg);
+   }
+   return 0;
+}
+
+static const char *socket_path;
+static int do_not_clean_up;
+
+static void cleanup(void)
+{
+   if (do_not_clean_up)
+   return;
+   unlink(socket_path);
+}
+
+static void cleanup_on_signal(int signo)
+{
+   cleanup();
+   sigchain_pop(signo);
+   raise(signo);
+}
+
+static void daemonize(void)
+{
+#ifdef NO_POSIX_GOODIES
+   die("fork not supported on this platform");
+#else
+   switch (fork()) {
+   case 0:
+   break;
+   case -1:
+   die_errno("fork failed");
+   default:
+   do_not_clean_up = 1;
+   exit(0);
+   }
+   if (setsid() == -1)
+   die_errno("setsid failed");
+   close(0);
+   close(1);
+   close(2);
+   sanitize_stdfds();
+#endif
+}
+
+int main(int argc, char **argv)
+{
+   struct strbuf sb = STRBUF_INIT;
+   struct sockaddr_un sun;
+   struct pollfd pfd[2];
+   int fd, err, nr;
+   int msgsize;
+   char *msg;
+   socklen_t vallen = sizeof(msgsize);
+   int no_daemon = 0;
+
+   if (!strcmp(argv[1], "--no-daemon")) {
+   no_daemon =1;
+   argv++;
+   argc--;
+   }
+   if (argc < 2)
+   die("insufficient arguments");
+   socket_path = argv[1];
+   memset(index_signature, 0, sizeof

[PATCH 6/6] file-watcher: support inotify

2014-01-12 Thread Nguyễn Thái Ngọc Duy
"git diff" on webkit:

no file watcher  1st run   subsequent runs
real0m1.361s0m1.445s  0m0.691s
user0m0.889s0m0.940s  0m0.649s
sys 0m0.469s0m0.495s  0m0.040s

Signed-off-by: Nguyễn Thái Ngọc Duy 
---
 config.mak.uname |   1 +
 file-watcher.c   | 139 +++
 2 files changed, 140 insertions(+)

diff --git a/config.mak.uname b/config.mak.uname
index 82d549e..603890d 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -33,6 +33,7 @@ ifeq ($(uname_S),Linux)
HAVE_PATHS_H = YesPlease
LIBC_CONTAINS_LIBINTL = YesPlease
HAVE_DEV_TTY = YesPlease
+   BASIC_CFLAGS += -DHAVE_INOTIFY
 endif
 ifeq ($(uname_S),GNU/kFreeBSD)
NO_STRLCPY = YesPlease
diff --git a/file-watcher.c b/file-watcher.c
index 35781fa..1512b46 100644
--- a/file-watcher.c
+++ b/file-watcher.c
@@ -3,17 +3,140 @@
 #include "string-list.h"
 #include "pkt-line.h"
 
+#ifdef HAVE_INOTIFY
+#include 
+#endif
+
 static char index_signature[41];
 static struct string_list updated = STRING_LIST_INIT_DUP;
 static int updated_sorted;
 
+#ifdef HAVE_INOTIFY
+
+static struct string_list watched_dirs = STRING_LIST_INIT_DUP;
+static int watched_dirs_sorted;
+static int inotify_fd;
+
+struct dir_info {
+   int wd;
+   struct string_list names;
+   int names_sorted;
+};
+
+static int handle_inotify(int fd)
+{
+   char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
+   struct inotify_event *event;
+   struct dir_info *dir;
+   struct string_list_item *item;
+   int i;
+   int len = read(fd, buf, sizeof(buf));
+   if (len < 0)
+   return -1;
+   event = (struct inotify_event *)buf;
+
+   if (len <= sizeof(struct inotify_event))
+   return 0;
+
+   for (i = 0; i < watched_dirs.nr; i++) {
+   struct dir_info *dir = watched_dirs.items[i].util;
+   if (dir->wd == event->wd)
+   break;
+   }
+   if (i == watched_dirs.nr)
+   return 0;
+   dir = watched_dirs.items[i].util;
+
+   if (!dir->names_sorted) {
+   sort_string_list(&dir->names);
+   dir->names_sorted = 1;
+   }
+   item = string_list_lookup(&dir->names, event->name);
+   if (item) {
+   if (!strcmp(watched_dirs.items[i].string, "."))
+   string_list_append(&updated, event->name);
+   else {
+   struct strbuf sb = STRBUF_INIT;
+   strbuf_addf(&sb, "%s/%s", watched_dirs.items[i].string,
+   item->string);
+   string_list_append(&updated, sb.buf);
+   updated_sorted = 0;
+   strbuf_release(&sb);
+   }
+
+   unsorted_string_list_delete_item(&dir->names,
+item - dir->names.items, 0);
+   if (dir->names.nr == 0) {
+   inotify_rm_watch(inotify_fd, dir->wd);
+   unsorted_string_list_delete_item(&watched_dirs, i, 1);
+   }
+   }
+   return 0;
+}
+
+static int watch_path(char *path)
+{
+   struct string_list_item *item;
+   char *sep = strrchr(path, '/');
+   struct dir_info *dir;
+   const char *dirname = ".";
+
+   if (sep) {
+   *sep = '\0';
+   dirname = path;
+   }
+
+   if (!watched_dirs_sorted) {
+   sort_string_list(&watched_dirs);
+   watched_dirs_sorted = 1;
+   }
+   item = string_list_lookup(&watched_dirs, dirname);
+   if (!item) {
+   int ret = inotify_add_watch(inotify_fd, dirname,
+   IN_ATTRIB | IN_DELETE | IN_MODIFY |
+   IN_MOVED_FROM | IN_MOVED_TO);
+   if (ret < 0)
+   return -1;
+   dir = xmalloc(sizeof(*dir));
+   memset(dir, 0, sizeof(*dir));
+   dir->wd = ret;
+   dir->names.strdup_strings = 1;
+   item = string_list_append(&watched_dirs, dirname);
+   item->util = dir;
+   }
+   dir = item->util;
+   string_list_append(&dir->names, sep ? sep + 1 : path);
+   dir->names_sorted = 0;
+   return 0;
+}
+
+static void reset_watches(void)
+{
+   int i;
+   for (i = 0; i < watched_dirs.nr; i++) {
+   struct dir_info *dir = watched_dirs.items[i].util;
+   inotify_rm_watch(inotify_fd, dir->wd);
+   string_list_clear(&dir->names, 0);
+   }
+   string_list_clear(&watched_dirs, 1);
+}
+
+#else
+
 static int watch_path(char *path)
 {
return -1;
 }
 
+static void reset_watches(void)
+{
+}
+
+#endif
+
 static void reset(const char *sig)
 {
+   reset_watches();
string_list_clear(&updated, 0);
strlcpy(inde

Re: Git: Question about specific subtree usage

2014-01-12 Thread Torsten Bögershausen
On 2014-01-12 01.23, THILLOSEN Andreas wrote:
> Hi,
> 
> I have a question about a specific use case for subtrees, but I'm not
I feel a little bit confused: Are you talking about git submodules?

And you may want to have a look at the repo tool:
https://code.google.com/p/git-repo/

HTH
/Torsten


--
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: Error in documentation for @{-n} in gitrevisions

2014-01-12 Thread Ramkumar Ramachandra
Kevin wrote:
> Either the documentation is wrong, and should be changed to "th
> branch/commit checkout out before the current one", or the behavior of
> @{-1} is wrong.

Yeah, the documentation needs to be updated. Patches welcome.
--
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