[RFC v2] blame: new option --prefer-first to better handle merged cherry-picks
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
> 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
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
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
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
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}
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
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
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
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
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
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
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
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
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
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
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
"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
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
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