Re: [PATCH v5] Add another option for receive.denyCurrentBranch
Hi Junio, On Sun, 30 Nov 2014, Junio C Hamano wrote: > Thanks, will queue. Thanks! > I think we would need a bit more tests to protect the feature from > future changes, if you care about the cleanliness requirement of > this feature which is a lot stricter than that of "git checkout". > > Perhaps like this one on top. Thanks again! Dscho -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] git-svn: Support for git-svn propset
This change allows git-svn to support setting subversion properties. Very useful for manually setting properties when committing to a subversion repo that *requires* properties to be set without requiring moving your changeset to separate subversion checkout in order to set props. This change is initially from David Fraser Appearing here: http://marc.info/?l=git&m=125259772625008&w=2 They are now forward ported to most recent git along with fixes to deal with files in subdirectories. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. Signed-off-by: Alfred Perlstein --- git-svn.perl | 50 +- perl/Git/SVN/Editor.pm | 47 +++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/git-svn.perl b/git-svn.perl index b6e2186..91423a8 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -115,7 +115,7 @@ my ($_stdin, $_help, $_edit, $_before, $_after, $_merge, $_strategy, $_preserve_merges, $_dry_run, $_parents, $_local, $_prefix, $_no_checkout, $_url, $_verbose, - $_commit_url, $_tag, $_merge_info, $_interactive); + $_commit_url, $_tag, $_merge_info, $_interactive, $_set_svn_props); # This is a refactoring artifact so Git::SVN can get at this git-svn switch. sub opt_prefix { return $_prefix || '' } @@ -193,6 +193,7 @@ my %cmd = ( 'dry-run|n' => \$_dry_run, 'fetch-all|all' => \$_fetch_all, 'commit-url=s' => \$_commit_url, + 'set-svn-props=s' => \$_set_svn_props, 'revision|r=i' => \$_revision, 'no-rebase' => \$_no_rebase, 'mergeinfo=s' => \$_merge_info, @@ -228,6 +229,9 @@ my %cmd = ( 'propget' => [ \&cmd_propget, 'Print the value of a property on a file or directory', { 'revision|r=i' => \$_revision } ], +'propset' => [ \&cmd_propset, + 'Set the value of a property on a file or directory - will be set on commit', + {} ], 'proplist' => [ \&cmd_proplist, 'List all properties of a file or directory', { 'revision|r=i' => \$_revision } ], @@ -1376,6 +1380,50 @@ sub cmd_propget { print $props->{$prop} . "\n"; } +# cmd_propset (PROPNAME, PROPVAL, PATH) +# +# Adjust the SVN property PROPNAME to PROPVAL for PATH. +sub cmd_propset { + my ($propname, $propval, $path) = @_; + $path = '.' if not defined $path; + $path = $cmd_dir_prefix . $path; + usage(1) if not defined $propname; + usage(1) if not defined $propval; + my $file = basename($path); + my $dn = dirname($path); + # diff has check_attr locally, so just call direct + #my $current_properties = check_attr( "svn-properties", $path ); + my $current_properties = Git::SVN::Editor::check_attr( "svn-properties", $path ); + my $new_properties = ""; + if ($current_properties eq "unset" || $current_properties eq "" || $current_properties eq "set") { + $new_properties = "$propname=$propval"; + } else { + # TODO: handle combining properties better + my @props = split(/;/, $current_properties); + my $replaced_prop = 0; + foreach my $prop (@props) { + # Parse 'name=value' syntax and set the property. + if ($prop =~ /([^=]+)=(.*)/) { + my ($n,$v) = ($1,$2); + if ($n eq $propname) +
[PATCH] git-svn: Support for propset
Hello folks, I have resurrected the code submitted by David Fraser to facilitate git-svn to set properties on files. This is my very first patch submission to git(1) so please be gentle. I have tried my best to abide with all the instructions located here: https://github.com/git/git/blob/master/Documentation/SubmittingPatches I've cc'd the top 4 people who have been active in the area: ~/git/git % git log --since '2010-01-01' git-svn.perl |grep Author | sort | uniq -c | sort -n | tail -4 6 Author: Eric Wong 9 Author: Jonathan Nieder 13 Author: Junio C Hamano 19 Author: Michael G. Schwern And of course: git@vger.kernel.org We (FreeBSD) and my company that uses FreeBSD are starting to use git-svn to collaborate with each other, however our (FreeBSD's) central repo is subversion and requires us to set properties in order to commit. This change would be tremendously helpful for a number of us in order to cut overhead of the git<->subversion bridge. Effectively with this patch we can commit directly to FreeBSD and FreeBSD ports using git. The next email should have the patch in format that is consumable by "git am" when applied against the master branch. I have been testing this and have done a number of commits directly to FreeBSD kernel and FreeBSD ports (packages) system using this patch successfully. Alfred Perlstein (1): git-svn: Support for git-svn propset git-svn.perl | 50 +- perl/Git/SVN/Editor.pm | 47 +++ 2 files changed, 96 insertions(+), 1 deletion(-) -- 2.1.2 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] compat: convert modes to use portable file type values
On 12/01/2014 04:40 AM, David Michael wrote: On Sun, Nov 30, 2014 at 3:16 PM, Torsten Bögershausen wrote: [snip] Could the code be more human-readable ? static inline mode_t mode_native_to_git(mode_t native_mode) { int perm_bits = native_mode & 0; if (S_ISREG(native_mode)) return 010 | perm_bits; if (S_ISDIR(native_mode)) return 004 | perm_bits; if (S_ISLNK(native_mode)) return 012 | perm_bits; if (S_ISBLK(native_mode)) return 006 | perm_bits; if (S_ISCHR(native_mode)) return 002 | perm_bits; if (S_ISFIFO(native_mode)) return 001 | perm_bits; /* Non-standard type bits were given. */ /* Shouldn't we die() here ?? */ return perm_bits; } Sure, I can send an updated patch with the new variable and without the "else"s. Regarding the question in the last comment: I was assuming if this case was ever reached, Git would handle the returned mode the same way as if it encountered an unknown/non-standard file type on a normal operating system, which could die() if needed in the function that called stat(). Should I send an updated patch that die()s there? David Not yet, please wait with a V2 patch until I finished my thinking ;-) I take back the suggestion with the die(). I was thinking how to handle unforeseen types, which may show up on the z/OS some day, So die() is not a good idea, it is better to ignore them, what the code does. Knowing that Git does not track block devices, nor character devices nor sockets, the above code could be simplyfied even more, by mapping everything which is not a directory, a file or a softlink to "device type 0) This is just a suggestion, I want to here from others as well: int perm_bits = native_mode & 0; if (S_ISREG(native_mode)) return 010 | perm_bits; if (S_ISDIR(native_mode)) return 004 | perm_bits; if (S_ISLNK(native_mode)) return 012 | perm_bits; /* Git does not track S_IFCHR, S_IFBLK, S_IFIFO, S_IFSOCK */ return perm_bits; -- 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: Deprecation warnings under XCode
On 12/01/2014 04:02 AM, Michael Blume wrote: I have no idea whether this should concern anyone, but my mac build of git shows CC imap-send.o imap-send.c:183:36: warning: 'ERR_error_string' is deprecated: first deprecated in OS X 10.7 [-Wdeprecated-declarations] fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), NULL)); ^ [] Isn't the warning a warning ;-) I don't see this warnings because my openssl comes from /opt/local/include (Mac ports) Does anybody know which new functions exist in Mac OS X versions >= 10.7 ? -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] introduce git root
Christian Couder writes: > I wonder if we could reuse "git config" which is already a "kitchen > synk" command to get/set a lot of parameters. I doubt it makes much sense. * Things like toplevel and cdup are not even something you configure. It is where you are, the current state of you. "git config" does not make any sense at all. * manpath and execpath and friends _might_ be something you may want to configure, and teach relevant codepaths to pay attention to the new configuration values. But until that happens, it does not make sense to have that information in "git config". "git config" does not show values that are not actually configured, so it won't be a replacement for "git --man-path" for those who do not have nonstandard place configured. So... -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] introduce git root
On Mon, Dec 1, 2014 at 4:04 AM, Junio C Hamano wrote: > > If I were redoing this today, I would probably nominate the "git" > potty as such a "kitchen synk" command. We have "--man-path" that > shows the location of the manual pages, "--exec-path[=path]" that > either shows or allows us to override the path to the subcommands, > and "--show-prefix", "--show-toplevel", and friends may feel quite > at home there. I wonder if we could reuse "git config" which is already a "kitchen synk" command to get/set a lot of parameters. Maybe we could dedicate a "git" or "virtual" or "proc" or "sys" (like /proc or /sys in Linux) namespace for these special config parameters that would not necessarily reflect something in the config file. "git config git.man-path" would be the same as "git --man-path". "git config git.root" would be the same as "git rev-parse --show-toplevel". "git config git.exec-path mypath" would allow us to override the path to the subcommands, probably by saving something in the config file. If we wanted for example to try just once a special exec-path we could use: git -c git.exec-path=/path/to/git-foo foo Best, Christian. -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] compat: convert modes to use portable file type values
On Sun, Nov 30, 2014 at 3:16 PM, Torsten Bögershausen wrote: [snip] > Could the code be more human-readable ? > static inline mode_t mode_native_to_git(mode_t native_mode) > { > int perm_bits = native_mode & 0; > if (S_ISREG(native_mode)) > return 010 | perm_bits; > if (S_ISDIR(native_mode)) > return 004 | perm_bits; > if (S_ISLNK(native_mode)) > return 012 | perm_bits; > if (S_ISBLK(native_mode)) > return 006 | perm_bits; > if (S_ISCHR(native_mode)) > return 002 | perm_bits; > if (S_ISFIFO(native_mode)) > return 001 | perm_bits; > /* Non-standard type bits were given. */ > /* Shouldn't we die() here ?? */ > return perm_bits; > } Sure, I can send an updated patch with the new variable and without the "else"s. Regarding the question in the last comment: I was assuming if this case was ever reached, Git would handle the returned mode the same way as if it encountered an unknown/non-standard file type on a normal operating system, which could die() if needed in the function that called stat(). Should I send an updated patch that die()s there? David -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v5] Add another option for receive.denyCurrentBranch
Thanks, will queue. I think we would need a bit more tests to protect the feature from future changes, if you care about the cleanliness requirement of this feature which is a lot stricter than that of "git checkout". Perhaps like this one on top. -- >8 -- From: Junio C Hamano Date: Sun, 30 Nov 2014 17:54:30 -0800 Subject: [PATCH] t5516: more tests for receive.denyCurrentBranch=updateInstead The previous one tests only the case where a path to be updated by the push-to-deploy has an incompatible change in the target's working tree that has already been added to the index, but the feature itself wants to require the working tree to be a lot cleaner than what is tested. Add a handful more tests to protect the feature from future changes that mistakenly (from the viewpoint of the inventor of the feature) loosens the cleanliness requirement, namely: - A change only to the working tree but not to the index is still a change to be protected; - An untracked file in the working tree that would be overwritten by a push-to-deploy needs to be protected; - A change that happens to make a file identical to what is being pushed is still a change to be protected (i.e. the feature's cleanliness requirement is more strict than that of checkout). Also, test that a stat-only change to the working tree is not a reason to reject a push-to-deploy. Signed-off-by: Junio C Hamano --- t/t5516-fetch-push.sh | 96 ++- 1 file changed, 87 insertions(+), 9 deletions(-) diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 7b353d0..85c7fec 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1332,28 +1332,106 @@ test_expect_success 'fetch into bare respects core.logallrefupdates' ' test_expect_success 'receive.denyCurrentBranch = updateInstead' ' git push testrepo master && - (cd testrepo && + ( + cd testrepo && git reset --hard && git config receive.denyCurrentBranch updateInstead ) && test_commit third path2 && + + # Try pushing into a repository with pristine working tree git push testrepo master && - test $(git rev-parse HEAD) = $(cd testrepo && git rev-parse HEAD) && - test third = "$(cat testrepo/path2)" && - (cd testrepo && + ( + cd testrepo && + git update-index -q --refresh && + git diff-files --quiet -- && + git diff-index --quiet --cached HEAD -- && + test third = "$(cat path2)" && + test $(git -C .. rev-parse HEAD) = $(git rev-parse HEAD) + ) && + + # Try pushing into a repository with working tree needing a refresh + ( + cd testrepo && + git reset --hard HEAD^ && + test $(git -C .. rev-parse HEAD^) = $(git rev-parse HEAD) && + test-chmtime +100 path1 + ) && + git push testrepo master && + ( + cd testrepo && git update-index -q --refresh && git diff-files --quiet -- && git diff-index --quiet --cached HEAD -- && - echo changed >path2 && - git add path2 + test_cmp ../path1 path1 && + test third = "$(cat path2)" && + test $(git -C .. rev-parse HEAD) = $(git rev-parse HEAD) ) && + + # Update what is to be pushed test_commit fourth path2 && + + # Try pushing into a repository with a dirty working tree + # (1) the working tree updated + ( + cd testrepo && + echo changed >path1 + ) && test_must_fail git push testrepo master && - test $(git rev-parse HEAD^) = $(git -C testrepo rev-parse HEAD) && - (cd testrepo && + ( + cd testrepo && + test $(git -C .. rev-parse HEAD^) = $(git rev-parse HEAD) && + git diff --quiet --cached && + test changed = "$(cat path1)" + ) && + + # (2) the index updated + ( + cd testrepo && + echo changed >path1 && + git add path1 + ) && + test_must_fail git push testrepo master && + ( + cd testrepo && + test $(git -C .. rev-parse HEAD^) = $(git rev-parse HEAD) && + git diff --quiet && + test changed = "$(cat path1)" + ) && + + # Introduce a new file in the update + test_commit fifth path3 && + + # (3) the working tree has an untracked file that would interfere + ( + cd testrepo && + git reset --hard && + echo changed >path3 + ) && + test_must_fail git push testrepo master && + ( + cd testrepo && + test $(git -C .. rev-parse HEAD^^) = $(git rev-parse HEAD) && + git diff --qu
Re: [PATCH] compat: convert modes to use portable file type values
David Michael writes: > This is my most recent attempt at solving the problem of z/OS using > different file type values than every other OS. I believe it should be > safe as long as the file type bits don't ever need to be converted back > to their native values (and I didn't see any instances of that). > > I've been testing it by making commits to the same repositories on > different operating systems and pushing those changes around, and so far > there have been no issues. > > Can anyone foresee any problems with this method? I cannot offhand comment on the last question above, but the reliance on exact S_IFxxx bit assignment was identified as a potential problem from very early days of Git that we have known about but didn't have need to address on any system that mattered. This is a long overdue issue and I am happy to see it getting tackled. The patch seems to be a sensible implementation of your design decision to use the one-way conversion. > diff --git a/compat/stat.c b/compat/stat.c > new file mode 100644 > index 000..0ff1f2f > --- /dev/null > +++ b/compat/stat.c > @@ -0,0 +1,49 @@ > +#define _POSIX_SOURCE > +#include /* NULL */ > +#include /* *stat, S_IS* */ > +#include /* mode_t */ > + > +static inline mode_t mode_native_to_git(mode_t native_mode) > +{ > + if (S_ISREG(native_mode)) > + return 010 | (native_mode & 0); > + else if (S_ISDIR(native_mode)) > + return 004 | (native_mode & 0); > + else if (S_ISLNK(native_mode)) > + return 012 | (native_mode & 0); > + else if (S_ISBLK(native_mode)) > + return 006 | (native_mode & 0); > + else if (S_ISCHR(native_mode)) > + return 002 | (native_mode & 0); > + else if (S_ISFIFO(native_mode)) > + return 001 | (native_mode & 0); > + else /* Non-standard type bits were given. */ > + return native_mode & 0; > +} -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] introduce git root
Matthieu Moy writes: > ... git rev-parse --show-toplevel is not just long, > it's just not the place where people would look for (it's neither about > revision nor about parsing, so clearly, "rev-parse" is not a good place > to host the feature in the UI). For the record, "rev-parse" is not about revisions in the first place. It started purely as a helper to implement "git log" in terms of "git rev-list" piped to "git diff-tree --stdin", which requires you to sift a command line argument list given to such a scripted "git log" into those to be passed to "rev-list" (i.e. primarily revision ranges) and those to be passed to "diff-tree" (i.e. primarily diff options and pathspecs). Various subcommands "rev-parse" takes such as --show-git-dir were added only because there wasn't any single best "kitchen sink" command to tuck such a small feature that do not deserve a standalone command (e.g. "git root", which makes it sound as if the top-level of the working tree is the most important thing in the world, and adding other useful things such as --show-prefix to it, while it would make sense to have them there from the implementation point of view, would be hard to justify against the connotation the word "root" gives us), and we happened to pick "rev-parse" as a "kitchen sink" place, which is not better or worse as anything else. If I were redoing this today, I would probably nominate the "git" potty as such a "kitchen synk" command. We have "--man-path" that shows the location of the manual pages, "--exec-path[=path]" that either shows or allows us to override the path to the subcommands, and "--show-prefix", "--show-toplevel", and friends may feel quite at home there. -- 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
Deprecation warnings under XCode
I have no idea whether this should concern anyone, but my mac build of git shows CC imap-send.o imap-send.c:183:36: warning: 'ERR_error_string' is deprecated: first deprecated in OS X 10.7 [-Wdeprecated-declarations] fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), NULL)); ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/openssl/err.h:279:7: note: 'ERR_error_string' has been explicitly marked deprecated here char *ERR_error_string(unsigned long e,char *buf) DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER; ^ imap-send.c:183:53: warning: 'ERR_get_error' is deprecated: first deprecated in OS X 10.7 [-Wdeprecated-declarations] fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), NULL)); ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/openssl/err.h:266:15: note: 'ERR_get_error' has been explicitly marked deprecated here unsigned long ERR_get_error(void) DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER; ^ imap-send.c:191:16: warning: 'SSL_get_error' is deprecated: first deprecated in OS X 10.7 [-Wdeprecated-declarations] int sslerr = SSL_get_error(sock->ssl, ret); ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/openssl/ssl.h:1506:5: note: 'SSL_get_error' has been explicitly marked deprecated here int SSL_get_error(const SSL *s,int ret_code) DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER; ^ imap-send.c:243:24: warning: 'X509_get_ext_d2i' is deprecated: first deprecated in OS X 10.7 [-Wdeprecated-declarations] if ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) { ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/openssl/x509.h:1151:8: note: 'X509_get_ext_d2i' has been explicitly marked deprecated here void* X509_get_ext_d2i(X509 *x, int nid, int *crit, int *idx) DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER; ^ imap-send.c:244:28: warning: 'sk_num' is deprecated: first deprecated in OS X 10.7 [-Wdeprecated-declarations] int num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names); ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/openssl/safestack.h:684:33: note: expanded from macro 'sk_GENERAL_NAME_num' #define sk_GENERAL_NAME_num(st) SKM_sk_num(GENERAL_NAME, (st)) ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/openssl/safestack.h:168:2: note: expanded from macro 'SKM_sk_num' sk_num(st) ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/openssl/stack.h:81:5: note: 'sk_num' has been explicitly marked deprecated here int sk_num(const STACK *) DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER; ^ imap-send.c:246:34: warning: 'sk_value' is deprecated: first deprecated in OS X 10.7 [-Wdeprecated-declarations] GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i); ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/openssl/safestack.h:685:38: note: expanded from macro 'sk_GENERAL_NAME_value' #define sk_GENERAL_NAME_value(st, i) SKM_sk_value(GENERAL_NAME, (st), (i)) ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/openssl/safestack.h:170:11: note: expanded from macro 'SKM_sk_value' ((type *)sk_value(st, i)) ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/openssl/stack.h:82:7: note: 'sk_value' has been explicitly marked deprecated here char *sk_value(const STACK *, int) DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER; ^ imap-send.c:252:3: warning: 'sk_pop_free' is deprecated: first deprecated in OS X 10.7 [-Wdeprecated-declarations] sk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free); ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/openssl/safestack.h:697:49: note: expanded from macro 'sk_GENERAL_NAME_pop_free' #define sk_GENERAL_NAME_pop_free(st, free_func) SKM_sk_pop_free(GENERAL_NAME, (st), (free_func)) ^ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platf
Re: Our cumbersome mailing list workflow
Michael Haggerty writes: > It seems like a few desirable features are being talked about here, and > summarizing the discussion as "centralized" vs "decentralized" is too > simplistic. What is really important? > > 1. Convenient and efficient, including for newcomers > 2. Usable while offline > 3. Usable in pure-text mode > 4. Decentralized > > Something else? As a reviewer / contributor (not speaking as the top maintainer), I would say that everything in one place, and for that one place mailbox is preferrable. "Somebody commented on (this instance of | the central) Gerrit, come look at it" is not usable; sending that comment out to those who work in their MUA, and allowing them to respond via their MUA probably adding their response as a new comment to Gerrit) would be usable. When I had to view a large-ish series by Ronnie on Gerrit, it was fairly painful. The interaction on an individual patch might be more convenient and efficient using a system like Gerrit than via e-mailed patch with reply messages, but as a vehicle to review a large series and see how the whole thing fits together, I did not find pages that made it usable (I am avoiding to say "I found it unusable", as that impression may be purely from that I couldn't find a more suitable pages that showed the same information in more usable form, i.e. user inexperience). Speaking of the "whole picture", I am hesitant to see us pushed into the "here is a central system (or here are federated systems) to handle only the patch reviews" direction; our changes result after discussing unrelated features, wishes, or bugs that happen outside of any specific patches with enough frequency, and that is why I prefer "everything in one place" aspect of the development based on the mailing list. That is not to say that the "one place" has forever to be the mailing list, though. But the tooling around an e-mail based workflow (e.g. marking threads as "worth revisiting" for later inspection, saving chosen messages into a mailbox and running "git am" on it) is already something I am used to. Whatever system we might end up migrating to, the convenience it offers has to beat the convenience of existing workflow to be worth switching to, at least to me as a reviewer/contributor. As the maintainer, I am not worried too much. As long as the mechanism can (1) reach "here is a series that is accepted by reviewers whose opinions are trusted" efficiently, and (2) allow me to queue the result without mistakes, I can go along with anything reasonable. -- 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: [PATCHv3 2/3] mailmap: use higher level string list functions
On 30.11.2014 16:47, Junio C Hamano wrote: > Eric Sunshine writes: > >> On Thu, Nov 27, 2014 at 1:44 PM, Michael Blume wrote: >>> The variable index seems to be unused/uninitialized now -- it's still >>> printed in debug messages, but if I'm reading correctly, its contents are >>> going to be nonsense. >> >> Nice catch. > > Let's do something like this squashed in, then. Michael, thanks for catching that! Junio, the squash-in looks fine with me. > > mailmap.c | 7 +++ > 1 file changed, 3 insertions(+), 4 deletions(-) > > diff --git a/mailmap.c b/mailmap.c > index 3b00a65..cb26af0 100644 > --- a/mailmap.c > +++ b/mailmap.c > @@ -90,8 +90,8 @@ static void add_mapping(struct string_list *map, > } > > if (old_name == NULL) { > - debug_mm("mailmap: adding (simple) entry for %s at index %d\n", > - old_email, index); > + debug_mm("mailmap: adding (simple) entry for '%s'\n", > old_email); > + > /* Replace current name and new email for simple entry */ > if (new_name) { > free(me->name); > @@ -103,8 +103,7 @@ static void add_mapping(struct string_list *map, > } > } else { > struct mailmap_info *mi = xcalloc(1, sizeof(struct > mailmap_info)); > - debug_mm("mailmap: adding (complex) entry for %s at index %d\n", > - old_email, index); > + debug_mm("mailmap: adding (complex) entry for '%s'\n", > old_email); > if (new_name) > mi->name = xstrdup(new_name); > if (new_email) > -- > 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 > -- 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: 'simple' push check that branch name matches does not work if push.default is unset (and hence implicitly simple)
Jeff King writes: > There is some other magic with "simple", too, around triangular > workflows. Describing it in detail would probably be too verbose in this > message, but we do refer to the description of push.default, which is > probably enough. Technically this new bit you are adding here is > covered there, too. But since we can improve the description by adding > such a small amount of text in this case, it seems like a reasonable > tradeoff. > > I suppose we could also customize the message based on the triangular > and non-triangular cases. I dunno. Yeah, I vaguely recall suggesting to polish advice message further to help users along a similar line, but probably that fell in the cracks. Thanks for a quick fix. -- 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: What's cooking in git.git (Nov 2014, #04; Wed, 26)
"brian m. carlson" writes: > On Wed, Nov 26, 2014 at 03:09:45PM -0800, Junio C Hamano wrote: >> * nd/untracked-cache (2014-10-27) 19 commits >> ... >> - dir.c: optionally compute sha-1 of a .gitignore file > > You didn't comment on the status of this branch, and I'm interested. I think we already saw a few comments responded by the author with "thanks, will include in a reroll", and that is why the listed are not even the latest round. The status was "waiting for a reroll". I haven't caught up with the traffic for the past few days, though. -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] commit: inform pre-commit if --amend is used
Mark Levedahl writes: > On 11/28/2014 12:18 AM, Jeff King wrote: > >> Thanks for the links; I had no recollection of that thread. >> Unsurprisingly, I like the "HEAD"/"HEAD~1" suggestion. That "peff" guy >> seems really clever (and handsome, too, I'll bet). >> >> I'd still be OK with any of the suggestions given in this thread, >> though. >> >> -Peff >> ars > > Apparently our combined handsome-foo was insufficient to get this > accepted way back when, hopefully the current submitter has more :^) > > In any event, I've carried the patches using HEAD/HEAD~1 in my tree > for the last 4+ years, have a widely used pre-commit script that > depends upon those. So, I personally would be very happy to see this > finally show up in Junio's tree, would prefer HEAD/HEAD~1 but can > adapt to whatever. One thing to be careful about is that the approach HEAD, HEAD~, etc. does allow this to be extended to cover merge cases as the old thread speculated, it will make it impossible to pass any kind of information, other than "here are the parents of the results", to the hook. Of course, there are ways to make sure that we won't paint us into an unescapable corner, e.g. an obvious example (not necessarily the best) being to pass "HEAD", "HEAD~", "b76b088 b260d26" etc., in other words, passing these parents still as a single argument, multiple parents concatenated with some delimiters, so that "$1" will always be "who are the parent(s)" even when we needed to later pass other sorts of information. -- 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: [PATCHv3 2/3] mailmap: use higher level string list functions
Eric Sunshine writes: > On Thu, Nov 27, 2014 at 1:44 PM, Michael Blume wrote: >> The variable index seems to be unused/uninitialized now -- it's still >> printed in debug messages, but if I'm reading correctly, its contents are >> going to be nonsense. > > Nice catch. Let's do something like this squashed in, then. mailmap.c | 7 +++ 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mailmap.c b/mailmap.c index 3b00a65..cb26af0 100644 --- a/mailmap.c +++ b/mailmap.c @@ -90,8 +90,8 @@ static void add_mapping(struct string_list *map, } if (old_name == NULL) { - debug_mm("mailmap: adding (simple) entry for %s at index %d\n", -old_email, index); + debug_mm("mailmap: adding (simple) entry for '%s'\n", old_email); + /* Replace current name and new email for simple entry */ if (new_name) { free(me->name); @@ -103,8 +103,7 @@ static void add_mapping(struct string_list *map, } } else { struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info)); - debug_mm("mailmap: adding (complex) entry for %s at index %d\n", -old_email, index); + debug_mm("mailmap: adding (complex) entry for '%s'\n", old_email); if (new_name) mi->name = xstrdup(new_name); if (new_email) -- 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/RFC v2] Squashed changes for multiple worktrees vs. submodules
builtin/checkout.c: use absolute path instead of given argument for picking worktree name, it happens to be needed because for submodule checkout the new worktree is always "." environment.c: add GIT_COMMON_DIR to local_repo_env git-submodule.sh: implement automatic cloning of main repository and checkout to new worktree at "submodule update --init" path.c, setup.c, submodule.c: fix "diff --submodule" when submodule is a linked worktree t/t7410-submodule-checkout-to.sh: tests for all the above Signed-off-by: Max Kirillov --- Hi. Thanks for including my 2 patches. But, while hacking the submodule init I became more convinced that the modules directory should be common and submodules in checkout should be a checkouts of the submodule. Because this is looks like concept of submodules, that they are unique for the lifetime of repository, even if they do not exist in all revisions. And if anybody want to use fully independent checkout they can be always checked out manually. Actually, after a submodule is initialized and have a proper gitlink, it can be updated and inquired regardless of where it points to. So that one I think is not needed. I have instead some changes to git-submodule, but have not prepared them yet as an exportable history. I am submitting here squashed changes which I have so far, to give an idea where it goes. I'll try to prepare a proper patch series as soon as I can. They contain change $gmane/258173, which I think is important, especially because it is required not only for initialization but for regular work also, and changes for initialization of submodules. They are rebased on top of you patches excluding the 34/34 patch. builtin/checkout.c | 25 ++--- cache.h | 1 + environment.c| 1 + git-submodule.sh | 94 ++ path.c | 24 - setup.c | 17 +++- submodule.c | 28 ++ t/t7410-submodule-checkout-to.sh | 201 +++ 8 files changed, 332 insertions(+), 59 deletions(-) create mode 100755 t/t7410-submodule-checkout-to.sh diff --git a/builtin/checkout.c b/builtin/checkout.c index 953b763..78154ae 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -858,27 +858,29 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, { struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT; struct strbuf sb = STRBUF_INIT; - const char *path = opts->new_worktree, *name; + struct strbuf sb_path = STRBUF_INIT; + const char *name; struct stat st; struct child_process cp; int counter = 0, len, ret; if (!new->commit) die(_("no branch specified")); - if (file_exists(path) && !is_empty_dir(path)) - die(_("'%s' already exists"), path); + strbuf_add_absolute_path(&sb_path, opts->new_worktree); + if (file_exists(sb_path.buf) && !is_empty_dir(sb_path.buf)) + die(_("'%s' already exists"), sb_path.buf); - len = strlen(path); - while (len && is_dir_sep(path[len - 1])) + len = sb_path.len; + while (len && is_dir_sep(sb_path.buf[len - 1])) len--; - for (name = path + len - 1; name > path; name--) + for (name = sb_path.buf + len - 1; name > sb_path.buf; name--) if (is_dir_sep(*name)) { name++; break; } strbuf_addstr(&sb_repo, - git_path("worktrees/%.*s", (int)(path + len - name), name)); + git_path("worktrees/%.*s", (int)(sb_path.buf + len - name), name)); len = sb_repo.len; if (safe_create_leading_directories_const(sb_repo.buf)) die_errno(_("could not create leading directories of '%s'"), @@ -906,11 +908,11 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, strbuf_addf(&sb, "%s/locked", sb_repo.buf); write_file(sb.buf, 1, "initializing\n"); - strbuf_addf(&sb_git, "%s/.git", path); + strbuf_addf(&sb_git, "%s/.git", sb_path.buf); if (safe_create_leading_directories_const(sb_git.buf)) die_errno(_("could not create leading directories of '%s'"), sb_git.buf); - junk_work_tree = xstrdup(path); + junk_work_tree = xstrdup(sb_path.buf); strbuf_reset(&sb); strbuf_addf(&sb, "%s/gitdir", sb_repo.buf); @@ -931,11 +933,11 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, write_file(sb.buf, 1, "../..\n"); if (!opts->quiet) - fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name); + fprintf_ln(stderr, _("Enter %s (identifier %s)"), sb_path.buf, name); setenv("GIT_CHECKOUT_NEW_WORKTREE", "1", 1); setenv(GIT_DIR_ENVIRONMENT,
Re: [PATCH] compat: convert modes to use portable file type values
On 2014-11-30 03.41, David Michael wrote: Some minor comments: > +static inline mode_t mode_native_to_git(mode_t native_mode) > +{ > + if (S_ISREG(native_mode)) > + return 010 | (native_mode & 0); > + else if (S_ISDIR(native_mode)) > + return 004 | (native_mode & 0); > + else if (S_ISLNK(native_mode)) > + return 012 | (native_mode & 0); > + else if (S_ISBLK(native_mode)) > + return 006 | (native_mode & 0); > + else if (S_ISCHR(native_mode)) > + return 002 | (native_mode & 0); > + else if (S_ISFIFO(native_mode)) > + return 001 | (native_mode & 0); > + else /* Non-standard type bits were given. */ > + return native_mode & 0; > +} Could the code be more human-readable ? static inline mode_t mode_native_to_git(mode_t native_mode) { int perm_bits = native_mode & 0; if (S_ISREG(native_mode)) return 010 | perm_bits; if (S_ISDIR(native_mode)) return 004 | perm_bits; if (S_ISLNK(native_mode)) return 012 | perm_bits; if (S_ISBLK(native_mode)) return 006 | perm_bits; if (S_ISCHR(native_mode)) return 002 | perm_bits; if (S_ISFIFO(native_mode)) return 001 | perm_bits; /* Non-standard type bits were given. */ /* Shouldn't we die() here ?? */ return perm_bits; } -- 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] t0027: check the eol conversion warnings
Depending on the file content, eol parameters and .gitattributes "git add" may give a warning when the eol of a file will change when the file is checked out again. There are 2 different warnings, either "CRLF will be replaced..." or "LF will be replaced...". Let t0027 check for these warnings: call create_file_in_repo() with additional parameters, which will be used to call check_warning(). When a file has eol=lf or eol=crlf in .gitattributes, it is handled as text and should be normalized. Add missing test cases in t0027. Signed-off-by: Torsten Bögershausen --- Changes since V1: - Simplified the diff - Fixed a bug (LF_mix_CR.err was mixed with CRLF_mix_LF) - Changed the commit message t/t0027-auto-crlf.sh | 82 ++-- 1 file changed, 66 insertions(+), 16 deletions(-) diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh index 2a4a6c1..452320d 100755 --- a/t/t0027-auto-crlf.sh +++ b/t/t0027-auto-crlf.sh @@ -55,16 +55,41 @@ create_gitattributes () { esac } +check_warning () { + case "$1" in + LF_CRLF) grep "LF will be replaced by CRLF" $2;; + CRLF_LF) grep "CRLF will be replaced by LF" $2;; + '') + >expect + grep "will be replaced by" $2 >actual + test_cmp expect actual + ;; + *) false ;; + esac +} + create_file_in_repo () { crlf=$1 attr=$2 + lfname=$3 + crlfname=$4 + lfmixcrlf=$5 + lfmixcr=$6 + crlfnul=$7 create_gitattributes "$attr" && + pfx=crlf_${crlf}_attr_${attr} for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul do - pfx=crlf_${crlf}_attr_${attr}_$f.txt && - cp $f $pfx && git -c core.autocrlf=$crlf add $pfx + fname=${pfx}_$f.txt && + cp $f $fname && + git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err" done && - git commit -m "core.autocrlf $crlf" + git commit -m "core.autocrlf $crlf" && + check_warning "$lfname" ${pfx}_LF.err && + check_warning "$crlfname" ${pfx}_CRLF.err && + check_warning "$lfmixcrlf" ${pfx}_CRLF_mix_LF.err && + check_warning "$lfmixcr" ${pfx}_LF_mix_CR.err && + check_warning "$crlfnul" ${pfx}_CRLF_nul.err } check_files_in_repo () { @@ -140,22 +165,47 @@ test_expect_success 'setup master' ' ' -test_expect_success 'create files' ' - create_file_in_repo false "" && - create_file_in_repo true "" && - create_file_in_repo input "" && - create_file_in_repo false "auto" && - create_file_in_repo true "auto" && - create_file_in_repo input "auto" && +warn_LF_CRLF="LF will be replaced by CRLF" +warn_CRLF_LF="CRLF will be replaced by LF" + +test_expect_success 'add files empty attr' ' + create_file_in_repo false "" """""""" "" && + create_file_in_repo true "" "LF_CRLF" """LF_CRLF" "" "" && + create_file_in_repo input "" """CRLF_LF" "CRLF_LF" "" "" +' + +test_expect_success 'add files attr=auto' ' + create_file_in_repo false "auto" """CRLF_LF" "CRLF_LF" "" "" && + create_file_in_repo true "auto" "LF_CRLF" """LF_CRLF" "" "" && + create_file_in_repo input "auto" """CRLF_LF" "CRLF_LF" "" "" +' + +test_expect_success 'add files attr=text' ' + create_file_in_repo false "text" """CRLF_LF" "CRLF_LF" "" "CRLF_LF" && + create_file_in_repo true "text" "LF_CRLF" """LF_CRLF" "LF_CRLF" ""&& + create_file_in_repo input "text" """CRLF_LF" "CRLF_LF" "" "CRLF_LF" +' + +test_expect_success 'add files attr=-text' ' + create_file_in_repo false "-text" "" """""" "" && + create_file_in_repo true "-text" "" """""" "" && + create_file_in_repo input "-text" "" """""" "" +' + +test_expect_success 'add files attr=lf' ' + create_file_in_repo false "lf""" "CRLF_LF" "CRLF_LF" "" "CRLF_LF" && + create_file_in_repo true "lf""" "CRLF_LF" "CRLF_LF" "" "CRLF_LF" && + create_file_in_repo input "lf""" "CRLF_LF" "CRLF_LF" "" "CRLF_LF" +' - create_file_in_repo false "text" && - create_file_in_repo true "text" && - create_file_in_repo input "text" && +test_expect_success 'add files attr=crlf' ' + create_file_in_repo false "crlf" "LF_CRLF" """LF_CRLF" "LF_CRLF" "" && + create_file_in_repo true "crlf" "LF_CRLF" """LF_CRLF" "LF_CRLF" "" && + create_file_in_repo input "crlf" "LF_CRLF" """LF_CRLF" "LF_CRLF" "" +' - create_file_in_repo false "-text" && - create_file_in_repo true "-text" && - create_file_in_repo input "-text" && +test_expect_success
Re: What's cooking in git.git (Nov 2014, #04; Wed, 26)
On Sun, Nov 30, 2014 at 03:35:33PM +0700, Duy Nguyen wrote: > I'm not Junio :) but I think the core changes are done. I wanted to > actually add watchman support on top of untracked cache as well to see > if it needs any more changes. I think I can see how it could be done > now (not easy, but not terribly hard). I'm going to resend soon to fix > some minor bugs (in a reroll that Junio has not picked up) and change > file format to be more compact. Excellent. I want to see if this improves performance over sshfs, but I don't want to enable it until I can put it on the server machine as well to prevent incompatibilities. -- brian m. carlson / brian with sandals: Houston, Texas, US +1 832 623 2791 | http://www.crustytoothpaste.net/~bmc | My opinion only OpenPGP: RSA v4 4096b: 88AC E9B2 9196 305B A994 7552 F1BA 225C 0223 B187 signature.asc Description: Digital signature
Re: [PATCH 24/34] checkout: reject if the branch is already checked out elsewhere
On 11/30/2014 03:24 AM, Nguyễn Thái Ngọc Duy wrote: One branch obviously can't be checked out at two places (but detached heads are ok). Give the user a choice in this case: --detach, -b new-branch, switch branch in the other checkout first or simply 'cd' and continue to work there. This seems too restrictive and is not "obvious" to me: I currently use git-new-workdir to have multiple checkouts of the same branch, with no ill effect. While those who do not understand what is going on underneath might be confused by one checkout suddenly showing uncommitted diffs, I don't accept that as a reason to outright prevent such use. I suggest, at the very least, that this behavior be overridden by a --force flag? Mark -- 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: Force git submodule update --remote for some branches
Am 28.11.2014 um 18:54 schrieb Timothy M. Redaelli: I have a repository with a submodule and I'd like to force git to checkout the LAST version of the submodule and not the stored one (like git submodule update --remote), but only on some branches and without the need to remember to add --remote every time you are on this branch. Is there any way to do it? Maybe using .gitmodules? Not yet. But yes, .gitmodules would be the right place to put such a configuration as then it would be per submodule and also per (superproject-)branch. By the way, what do you think the output of git status and git diff should look like when you updated a submodule to its branch tip which differs from the commit recorded in the superproject? Should these changes be displayed or not? And only if the new tip is a fast-forward of the old or always? -- 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: Thinning a repository
On 11/30/2014 01:34 PM, Fredrik Gustafsson wrote: > On Sun, Nov 30, 2014 at 01:18:34PM +0100, Yuri D'Elia wrote: >> Is there a quick way to reproduce the effect of a shallow clone on a >> local repository that doesn't involve filter-branch and/or re-clone? > > I'm curious, why is it a bad thing to do a re-clone? If you clone your > local repo it would be really fast. I see no reason to involve the workspace. I would also want to keep the current remote/origin as it is. I expect a 'thin' command to be faster by not touching the workspace at all. -- 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: Thinning a repository
On Sun, Nov 30, 2014 at 01:18:34PM +0100, Yuri D'Elia wrote: > Is there a quick way to reproduce the effect of a shallow clone on a > local repository that doesn't involve filter-branch and/or re-clone? I'm curious, why is it a bad thing to do a re-clone? If you clone your local repo it would be really fast. -- Med vänlig hälsning Fredrik Gustafsson tel: 0733-608274 e-post: iv...@iveqy.com -- 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
Thinning a repository
Hi everyone, Is there a quick way to reproduce the effect of a shallow clone on a local repository that doesn't involve filter-branch and/or re-clone? My motivation is to reduce the local size of repositories I'm only following, by trimming the history without prejudice to a [N] set of last commits. It feels stupid that the quickest way I'm aware of right now to achieve this is to "git clone --depth N ..." again. filter-branch is ridiculously slow, as it iterates through history. I've tried using graft points, but the combination of: echo [sha] > .git/info/grafts git reflog expire --expire=0 --all git repack -Ad doesn't really save any space and/or reduce the object count as I would expect. It means there's probably still reachable? I'd really love to have a 'git thin [depth]' subcommand to perform the above however. I don't really want to have to iterate through refs just to check if they are still reachable within [n] commits just to delete them. Thanks for any pointer. -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] introduce git root
Arjun Sreedharan writes: > On 30 November 2014 at 04:38, Philip Oakley wrote: >> From: "Arjun Sreedharan" >>> >>> This introduces `git root` which outputs the root directory >>> (the directory that contains .git). >>> The same can be accomplished by `git rev-parse --show-toplevel`. >>> `git root` is much more intuitive and easy to remember. >>> All it does is set the arguments for rev-parse >> >> >> This may be better as an alias. >> I've added it to my aliases list. >> > > I know that. I am suggesting this to be a built-in command, without having the > need to add as an alias. Indeed, suggesting people to add an alias does not solve the discoverability issue. git rev-parse --show-toplevel is not just long, it's just not the place where people would look for (it's neither about revision nor about parsing, so clearly, "rev-parse" is not a good place to host the feature in the UI). If we were to rewrite Git from scratch, then I would be all for having a "git root" command. Given that we already have rev-parse --show-toplevel, and that we'll have to keep it anyway for backward compatibility, I'm a bit more hesitant ("Git is hard to use because it doesn't have enough commands" is not a complain I hear so often ;-) ), but still mostly positive. If we go this way, then the documentation must be updated too. I think the doc should still recommend "git rev-parse --show-toplevel" for scripting until Git versions implementing "git root" are widely deployed enough. Also, there are other options of git rev-parse which should be dealt with: at least --show-cdup (could be eg. "git root --relative") and --show-prefix, but probably also others from the "Options for Files" in the man of git-rev-parse. -- Matthieu Moy http://www-verimag.imag.fr/~moy/ -- 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] ls-tree: disable negative pathspec because it's not supported
Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/ls-tree.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 053edb2..3b04a0f 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -174,7 +174,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) * cannot be lifted until it is converted to use * match_pathspec() or tree_entry_interesting() */ - parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE, + parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE | + PATHSPEC_EXCLUDE, PATHSPEC_PREFER_CWD, prefix, argv + 1); for (i = 0; i < pathspec.nr; i++) -- 2.2.0.60.gb7b3c64 -- 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] ls-tree: remove path filtering logic in show_tree
ls-tree uses read_tree_recursive() which already does path filtering using pathspec. No need to filter one more time based on prefix only. "ls-tree ../somewhere" does not work because of this. write_name_quotedpfx() can now be retired because nobody else uses it. Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/ls-tree.c| 14 +++--- quote.c | 21 - quote.h | 2 -- t/t3102-ls-tree-wildcards.sh | 8 4 files changed, 15 insertions(+), 30 deletions(-) diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 1ab0381..053edb2 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -65,6 +65,7 @@ static int show_tree(const unsigned char *sha1, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *context) { int retval = 0; + int baselen; const char *type = blob_type; if (S_ISGITLINK(mode)) { @@ -89,11 +90,6 @@ static int show_tree(const unsigned char *sha1, struct strbuf *base, else if (ls_options & LS_TREE_ONLY) return 0; - if (chomp_prefix && - (base->len < chomp_prefix || -memcmp(ls_tree_prefix, base->buf, chomp_prefix))) - return 0; - if (!(ls_options & LS_NAME_ONLY)) { if (ls_options & LS_SHOW_SIZE) { char size_text[24]; @@ -113,8 +109,12 @@ static int show_tree(const unsigned char *sha1, struct strbuf *base, printf("%06o %s %s\t", mode, type, find_unique_abbrev(sha1, abbrev)); } - write_name_quotedpfx(base->buf + chomp_prefix, base->len - chomp_prefix, - pathname, stdout, line_termination); + baselen = base->len; + strbuf_addstr(base, pathname); + write_name_quoted_relative(base->buf, + chomp_prefix ? ls_tree_prefix : NULL, + stdout, line_termination); + strbuf_setlen(base, baselen); return retval; } diff --git a/quote.c b/quote.c index 45e3db1..7920e18 100644 --- a/quote.c +++ b/quote.c @@ -274,27 +274,6 @@ void write_name_quoted(const char *name, FILE *fp, int terminator) fputc(terminator, fp); } -void write_name_quotedpfx(const char *pfx, size_t pfxlen, - const char *name, FILE *fp, int terminator) -{ - int needquote = 0; - - if (terminator) { - needquote = next_quote_pos(pfx, pfxlen) < pfxlen - || name[next_quote_pos(name, -1)]; - } - if (needquote) { - fputc('"', fp); - quote_c_style_counted(pfx, pfxlen, NULL, fp, 1); - quote_c_style(name, NULL, fp, 1); - fputc('"', fp); - } else { - fwrite(pfx, pfxlen, 1, fp); - fputs(name, fp); - } - fputc(terminator, fp); -} - void write_name_quoted_relative(const char *name, const char *prefix, FILE *fp, int terminator) { diff --git a/quote.h b/quote.h index 71dcc3a..99e04d3 100644 --- a/quote.h +++ b/quote.h @@ -56,8 +56,6 @@ extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq extern void quote_two_c_style(struct strbuf *, const char *, const char *, int); extern void write_name_quoted(const char *name, FILE *, int terminator); -extern void write_name_quotedpfx(const char *pfx, size_t pfxlen, - const char *name, FILE *, int terminator); extern void write_name_quoted_relative(const char *name, const char *prefix, FILE *fp, int terminator); diff --git a/t/t3102-ls-tree-wildcards.sh b/t/t3102-ls-tree-wildcards.sh index c286854..83fca8d 100755 --- a/t/t3102-ls-tree-wildcards.sh +++ b/t/t3102-ls-tree-wildcards.sh @@ -19,4 +19,12 @@ EOF test_cmp expected actual ' +test_expect_success 'ls-tree outside prefix' ' + cat >expected
[PATCH 1/3] tree.c: update read_tree_recursive callback to pass strbuf as base
This allows the callback to use 'base' as a temporary buffer to quickly assemble full path "without" extra allocation. The callback has to restore it afterwards of course. Helped-by: Eric Sunshine Signed-off-by: Nguyễn Thái Ngọc Duy --- archive.c | 34 +- builtin/checkout.c | 8 builtin/log.c | 2 +- builtin/ls-tree.c | 9 + merge-recursive.c | 15 ++- tree.c | 16 +++- tree.h | 3 ++- 7 files changed, 50 insertions(+), 37 deletions(-) diff --git a/archive.c b/archive.c index 94a9981..9e30246 100644 --- a/archive.c +++ b/archive.c @@ -157,18 +157,26 @@ static int write_archive_entry(const unsigned char *sha1, const char *base, return write_entry(args, sha1, path.buf, path.len, mode); } +static int write_archive_entry_buf(const unsigned char *sha1, struct strbuf *base, + const char *filename, unsigned mode, int stage, + void *context) +{ + return write_archive_entry(sha1, base->buf, base->len, +filename, mode, stage, context); +} + static void queue_directory(const unsigned char *sha1, - const char *base, int baselen, const char *filename, + struct strbuf *base, const char *filename, unsigned mode, int stage, struct archiver_context *c) { struct directory *d; - d = xmallocz(sizeof(*d) + baselen + 1 + strlen(filename)); + d = xmallocz(sizeof(*d) + base->len + 1 + strlen(filename)); d->up = c->bottom; - d->baselen = baselen; + d->baselen = base->len; d->mode= mode; d->stage = stage; c->bottom = d; - d->len = sprintf(d->path, "%.*s%s/", baselen, base, filename); + d->len = sprintf(d->path, "%.*s%s/", (int)base->len, base->buf, filename); hashcpy(d->sha1, sha1); } @@ -191,28 +199,28 @@ static int write_directory(struct archiver_context *c) } static int queue_or_write_archive_entry(const unsigned char *sha1, - const char *base, int baselen, const char *filename, + struct strbuf *base, const char *filename, unsigned mode, int stage, void *context) { struct archiver_context *c = context; while (c->bottom && - !(baselen >= c->bottom->len && -!strncmp(base, c->bottom->path, c->bottom->len))) { + !(base->len >= c->bottom->len && +!strncmp(base->buf, c->bottom->path, c->bottom->len))) { struct directory *next = c->bottom->up; free(c->bottom); c->bottom = next; } if (S_ISDIR(mode)) { - queue_directory(sha1, base, baselen, filename, + queue_directory(sha1, base, filename, mode, stage, c); return READ_TREE_RECURSIVE; } if (write_directory(c)) return -1; - return write_archive_entry(sha1, base, baselen, filename, mode, + return write_archive_entry(sha1, base->buf, base->len, filename, mode, stage, context); } @@ -260,7 +268,7 @@ int write_archive_entries(struct archiver_args *args, err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec, args->pathspec.has_wildcard ? queue_or_write_archive_entry : - write_archive_entry, + write_archive_entry_buf, &context); if (err == READ_TREE_RECURSIVE) err = 0; @@ -286,14 +294,14 @@ static const struct archiver *lookup_archiver(const char *name) return NULL; } -static int reject_entry(const unsigned char *sha1, const char *base, - int baselen, const char *filename, unsigned mode, +static int reject_entry(const unsigned char *sha1, struct strbuf *base, + const char *filename, unsigned mode, int stage, void *context) { int ret = -1; if (S_ISDIR(mode)) { struct strbuf sb = STRBUF_INIT; - strbuf_addstr(&sb, base); + strbuf_addbuf(&sb, base); strbuf_addstr(&sb, filename); if (!match_pathspec(context, sb.buf, sb.len, 0, NULL, 1)) ret = READ_TREE_RECURSIVE; diff --git a/builtin/checkout.c b/builtin/checkout.c index 5410dac..8adf48d 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -62,7 +62,7 @@ static int post_checkout_hook(struct commit *old, struct commit *new, } -static int update_some(const unsigned char *sha1, const char *base, int baselen, +static int update_some(const unsigned char *sha1, struct strbuf *base, const char *pathname, unsigned mode, int stage, void *contex
[PATCH 0/3] ls-tree fixes
The first two fix ls-tree's unable to handle relative paths outside $PWD. The last one rejects negative pathspec. This is a resend from http://thread.gmane.org/gmane.comp.version-control.git/259233/focus=259264 Nguyễn Thái Ngọc Duy (3): tree.c: update read_tree_recursive callback to pass strbuf as base ls-tree: remove path filtering logic in show_tree ls-tree: disable negative pathspec because it's not supported archive.c| 34 +- builtin/checkout.c | 8 builtin/log.c| 2 +- builtin/ls-tree.c| 20 +++- merge-recursive.c| 15 ++- quote.c | 21 - quote.h | 2 -- t/t3102-ls-tree-wildcards.sh | 8 tree.c | 16 +++- tree.h | 3 ++- 10 files changed, 64 insertions(+), 65 deletions(-) -- 2.2.0.60.gb7b3c64 -- 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 15/19] list-files: do not show duplicate cached entries
With the current show_files() "list-files -tcm" will show foo.c M foo.c The first item is redundant. If "foo.c" is modified, we know it's in the cache. Introduce show_files_compact to do that because ls-files is plumbing and scripts may already depend on current display behavior. Another difference in show_files_compact() is it does not show skip-worktree (aka outside sparse checkout) entries anymore, which makes sense in porcelain context. Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/ls-files.c | 52 +++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 42c530d..fc70265 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -333,6 +333,53 @@ static void show_files(struct dir_struct *dir) } } +static void show_files_compact(struct dir_struct *dir) +{ + int i; + + /* For cached/deleted files we don't need to even do the readdir */ + if (show_others || show_killed) { + if (!show_others) + dir->flags |= DIR_COLLECT_KILLED_ONLY; + fill_directory(dir, &pathspec); + if (show_others) + show_other_files(dir); + if (show_killed) + show_killed_files(dir); + } + if (!(show_cached || show_stage || show_deleted || show_modified)) + return; + for (i = 0; i < active_nr; i++) { + const struct cache_entry *ce = active_cache[i]; + struct stat st; + int err, shown = 0; + if ((dir->flags & DIR_SHOW_IGNORED) && + !ce_excluded(dir, ce)) + continue; + if (show_unmerged && !ce_stage(ce)) + continue; + if (ce->ce_flags & CE_UPDATE) + continue; + if (ce_skip_worktree(ce)) + continue; + err = lstat(ce->name, &st); + if (show_deleted && err) { + show_ce_entry(tag_removed, ce); + shown = 1; + } + if (show_modified && (err || ce_modified(ce, &st, 0))) { + show_ce_entry(tag_modified, ce); + shown = 1; + } + if (ce_stage(ce)) { + show_ce_entry(tag_unmerged, ce); + shown = 1; + } + if (!shown && show_cached) + show_ce_entry(tag_cached, ce); + } +} + /* * Prune the index to only contain stuff starting with "prefix" */ @@ -743,7 +790,10 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL); setup_pager(); } - show_files(&dir); + if (porcelain) + show_files_compact(&dir); + else + show_files(&dir); if (show_resolve_undo) show_ru_info(); -- 2.2.0.60.gb7b3c64 -- 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 13/19] list-files: add -t back
Tag "H" (cached) is not shown though because it's usually the majority and becomes noise. Not showing it makes the other tags stand out. -t is on by default if more than one file category is selected. Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/git-list-files.txt | 6 ++ builtin/ls-files.c | 27 +-- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt index 725a236..0ef616b 100644 --- a/Documentation/git-list-files.txt +++ b/Documentation/git-list-files.txt @@ -45,6 +45,12 @@ OPTIONS --unmerged:: Show unmerged files +-t:: +--[no-]tag:: + Show a tag to indicate file type. Automatically turned on with + multiple file selections. See linkgit::git-ls-files[1] option + `-t` for more information. + -R:: --recursive:: Equivalent of `--max-depth=-1` (infinite recursion). diff --git a/builtin/ls-files.c b/builtin/ls-files.c index be9a39c..c7aaade 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -596,6 +596,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) N_("show untracked files")), OPT_SET_INT('R', "recursive", &max_depth, N_("shortcut for --max-depth=-1"), -1), + OPT_BOOL('t', "tag", &show_tag, + N_("identify the file status with tags")), OPT_BIT('i', "ignored", &dir.flags, N_("show ignored files"), DIR_SHOW_IGNORED), @@ -636,6 +638,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) setup_standard_excludes(&dir); use_color = -1; max_depth = 0; + show_tag = -1; git_config(git_ls_config, NULL); } else git_config(git_default_config, NULL); @@ -648,16 +651,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) for (i = 0; i < exclude_list.nr; i++) { add_exclude(exclude_list.items[i].string, "", 0, el, --exclude_args); } - if (show_tag || show_valid_bit) { - tag_cached = "H "; - tag_unmerged = "M "; - tag_removed = "R "; - tag_modified = "C "; - tag_other = "? "; - tag_killed = "K "; - tag_skip_worktree = "S "; - tag_resolve_undo = "U "; - } if (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed) require_work_tree = 1; if (show_unmerged && !porcelain) @@ -711,6 +704,20 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) show_killed || show_modified || show_resolve_undo)) show_cached = 1; + if (show_tag == -1) + show_tag = (show_cached + show_deleted + show_others + + show_unmerged + show_killed + show_modified) > 1; + if (show_tag || show_valid_bit) { + tag_cached = porcelain ? " " : "H "; + tag_unmerged = "M "; + tag_removed = "R "; + tag_modified = "C "; + tag_other = "? "; + tag_killed = "K "; + tag_skip_worktree = "S "; + tag_resolve_undo = "U "; + } + if (max_prefix) prune_cache(max_prefix); if (with_tree) { -- 2.2.0.60.gb7b3c64 -- 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 16/19] list-files: show directories as well as files
The index does not store directories explicitly (except submodules) so we have to figure them out from file list. The function show_directories() deliberately generates duplicate directories and expects the previous patch to remove duplicates. Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/ls-files.c | 44 +++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index fc70265..41efdaa 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -27,6 +27,8 @@ static int show_resolve_undo; static int show_modified; static int show_killed; static int show_valid_bit; +static int show_tag; +static int show_dirs; static int line_terminator = '\n'; static int debug_mode; static int use_color; @@ -333,6 +335,43 @@ static void show_files(struct dir_struct *dir) } } +static void show_directories(const struct cache_entry *ce) +{ + static const char *last_directory; + struct strbuf sb = STRBUF_INIT; + const char *p = ce->name + prefix_len; + const char *sep; + + if (last_directory) { + int len = strlen(last_directory); + if (!strncmp(ce->name, last_directory, len) && + ce->name[len] == '/') + p += len + 1; + } + + while (*p && (sep = strchr(p, '/'))) { + struct strbuf sb2 = STRBUF_INIT; + strbuf_reset(&sb); + strbuf_add(&sb, ce->name, sep - ce->name); + p = sep + 1; + if (!match_pathspec(&pathspec, sb.buf, sb.len, + prefix_len, NULL, 1)) + continue; + write_name(&sb2, sb.buf); + if (want_color(use_color)) { + struct strbuf sb3 = STRBUF_INIT; + color_filename(&sb3, ce->name, sb2.buf, S_IFDIR, 1); + strbuf_release(&sb2); + sb2 = sb3; + } + if (show_tag) + strbuf_insert(&sb2, 0, tag_cached, strlen(tag_cached)); + last_directory = strbuf_detach(&sb, NULL); + strbuf_fputs(&sb2, last_directory, NULL); + strbuf_release(&sb2); + } +} + static void show_files_compact(struct dir_struct *dir) { int i; @@ -353,6 +392,8 @@ static void show_files_compact(struct dir_struct *dir) const struct cache_entry *ce = active_cache[i]; struct stat st; int err, shown = 0; + if (show_dirs) + show_directories(ce); if ((dir->flags & DIR_SHOW_IGNORED) && !ce_excluded(dir, ce)) continue; @@ -575,7 +616,7 @@ static int git_ls_config(const char *var, const char *value, void *cb) int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) { - int require_work_tree = 0, show_tag = 0, i; + int require_work_tree = 0, i; int max_depth = -1; const char *max_prefix; struct dir_struct dir; @@ -696,6 +737,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) use_color = -1; max_depth = 0; show_tag = -1; + show_dirs = 1; git_config(git_ls_config, NULL); } else git_config(git_default_config, NULL); -- 2.2.0.60.gb7b3c64 -- 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
git@vger.kernel.org
Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/git-list-files.txt | 4 ++-- builtin/ls-files.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt index 22084eb..c57129b 100644 --- a/Documentation/git-list-files.txt +++ b/Documentation/git-list-files.txt @@ -53,8 +53,8 @@ OPTIONS -F:: --classify:: - Append indicator (one of `*/=>@|`, which is executable, - directory, socket, Solaris door, symlink, or fifo + Append indicator (one of `*/=>@|&`, which is executable, + directory, socket, Solaris door, symlink, fifo, or submodule respectively) to entries. -R:: diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 28737cb..5b5a068 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -92,6 +92,8 @@ static void append_indicator(struct strbuf *sb, mode_t mode) c = '|'; else if (S_ISSOCK(mode)) c = '='; + else if (S_ISGITLINK(mode)) + c = '&'; #ifdef S_ISDOOR else if (S_ISDOOR(mode)) c = '>'; -- 2.2.0.60.gb7b3c64 -- 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 19/19] list-files: -M aka diff-cached
Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/ls-files.c | 57 +++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 5b5a068..08ae206 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -16,6 +16,9 @@ #include "pathspec.h" #include "color.h" #include "column.h" +#include "diff.h" +#include "diffcore.h" +#include "revision.h" static int abbrev; static int show_deleted; @@ -25,6 +28,7 @@ static int show_stage; static int show_unmerged; static int show_resolve_undo; static int show_modified; +static int show_diff_cached; static int show_killed; static int show_valid_bit; static int show_tag; @@ -53,6 +57,7 @@ static const char *tag_removed = ""; static const char *tag_other = ""; static const char *tag_killed = ""; static const char *tag_modified = ""; +static const char *tag_diff_cached = ""; static const char *tag_skip_worktree = ""; static const char *tag_resolve_undo = ""; @@ -343,7 +348,7 @@ static void show_files(struct dir_struct *dir) (ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce); } } - if (show_deleted || show_modified) { + if (show_deleted || show_modified || show_diff_cached) { for (i = 0; i < active_nr; i++) { const struct cache_entry *ce = active_cache[i]; struct stat st; @@ -358,7 +363,15 @@ static void show_files(struct dir_struct *dir) err = lstat(ce->name, &st); if (show_deleted && err) show_ce_entry(tag_removed, ce); - if (show_modified && ce_modified(ce, &st, 0)) + if (show_diff_cached && (ce->ce_flags & CE_MATCHED)) { + show_ce_entry(tag_diff_cached, ce); + /* +* if we don't clear, it'll confuse write_ce_name() +* when show_ce_entry(tag_modified, ce) is called +*/ + active_cache[i]->ce_flags &= ~CE_MATCHED; + } + if (show_modified && (err || ce_modified(ce, &st, 0))) show_ce_entry(tag_modified, ce); } } @@ -452,6 +465,38 @@ static void show_files_compact(struct dir_struct *dir) } } +static void mark_diff_cached(struct diff_queue_struct *q, +struct diff_options *options, +void *data) +{ + int i; + + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + int pos = cache_name_pos(p->two->path, strlen(p->two->path)); + if (pos < 0) + continue; + active_cache[pos]->ce_flags |= CE_MATCHED; + } +} + +static void diff_cached(struct pathspec *pathspec) +{ + struct rev_info rev; + const char *argv[] = { "ls-files", "HEAD", NULL }; + + init_revisions(&rev, NULL); + setup_revisions(2, argv, &rev, NULL); + + rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; + rev.diffopt.format_callback = mark_diff_cached; + rev.diffopt.detect_rename = 1; + rev.diffopt.rename_limit = 200; + rev.diffopt.break_opt = 0; + copy_pathspec(&rev.prune_data, pathspec); + run_diff_index(&rev, 1); +} + /* * Prune the index to only contain stuff starting with "prefix" */ @@ -721,6 +766,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) N_("show cached files that are deleted on working directory")), OPT_BOOL('m', "modified", &show_modified, N_("show cached files that have modification on working directory")), + OPT_BOOL('M', "modified", &show_diff_cached, + N_("show modified files in the cache")), OPT_BOOL('o', "others", &show_others, N_("show untracked files")), OPT_SET_INT('R', "recursive", &max_depth, @@ -833,11 +880,12 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) /* With no flags, we default to showing the cached files */ if (!(show_stage || show_deleted || show_others || show_unmerged || - show_killed || show_modified || show_resolve_undo)) + show_killed || show_modified || show_resolve_undo || show_diff_cached)) show_cached = 1; if (show_tag == -1) show_tag = (show_cached + show_deleted + show_others + + show_diff_cached + show_unmerged + show_killed + show_modified) > 1; if (show_tag || show_valid_bit) { tag_cached = por
[PATCH 14/19] list-files: sort output and remove duplicates
When you mix different file types, with ls-files you may get separate listing. For example, "ls-files -cm" will show file "abc" twice: one as part of cached list, one of modified list. With "ls" (and this patch) they will be in a single sorted list (easier for the eye). Duplicate entries are also removed. Note that display content is compared, so if you have "-t" on, or you color file types differently, you will get duplicate textual entries. This is good imo. Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/ls-files.c | 36 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index c7aaade..42c530d 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -53,6 +53,13 @@ static const char *tag_modified = ""; static const char *tag_skip_worktree = ""; static const char *tag_resolve_undo = ""; +static int compare_output(const void *a_, const void *b_) +{ + const struct string_list_item *a = a_; + const struct string_list_item *b = b_; + return strcmp(a->util, b->util); +} + static void write_name(struct strbuf *sb, const char *name) { /* @@ -68,10 +75,12 @@ static void write_name(struct strbuf *sb, const char *name) quote_path_relative(name, real_prefix, sb); } -static void strbuf_fputs(struct strbuf *sb, FILE *fp) +static void strbuf_fputs(struct strbuf *sb, const char *full_name, FILE *fp) { - if (column_active(colopts)) { - string_list_append(&output, strbuf_detach(sb, NULL)); + if (column_active(colopts) || porcelain) { + struct string_list_item *it; + it = string_list_append(&output, strbuf_detach(sb, NULL)); + it->util = (void*)full_name; return; } fwrite(sb->buf, sb->len, 1, fp); @@ -106,7 +115,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) strbuf_reset(&sb); strbuf_addstr(&sb, tag); write_dir_entry(&sb, ent); - strbuf_fputs(&sb, stdout); + strbuf_fputs(&sb, ent->name, stdout); } static void show_other_files(struct dir_struct *dir) @@ -223,7 +232,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce) ce_stage(ce)); } write_ce_name(&sb, ce); - strbuf_fputs(&sb, stdout); + strbuf_fputs(&sb, ce->name, stdout); if (debug_mode) { const struct stat_data *sd = &ce->ce_stat_data; @@ -524,6 +533,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) const char *max_prefix; struct dir_struct dir; struct exclude_list *el; + struct column_options copts; struct string_list exclude_list = STRING_LIST_INIT_NODUP; struct option builtin_ls_files_options[] = { { OPTION_CALLBACK, 'z', NULL, NULL, NULL, @@ -671,7 +681,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (debug_mode) die(_("--column and --debug are incompatible")); } - if (column_active(colopts)) + if (column_active(colopts) || porcelain) line_terminator = 0; if (require_work_tree && !is_inside_work_tree()) @@ -737,13 +747,15 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (show_resolve_undo) show_ru_info(); - if (column_active(colopts)) { - struct column_options copts; - memset(&copts, 0, sizeof(copts)); - copts.padding = 2; - print_columns(&output, colopts, &copts); - string_list_clear(&output, 0); + memset(&copts, 0, sizeof(copts)); + copts.padding = 2; + if (porcelain) { + qsort(output.items, output.nr, sizeof(*output.items), + compare_output); + string_list_remove_duplicates(&output, 0); } + print_columns(&output, colopts, &copts); + string_list_clear(&output, 0); if (ps_matched) { int bad; -- 2.2.0.60.gb7b3c64 -- 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 17/19] list-files: add -F/--classify
This appends an indicator after the file name if it's executable, a directory and so on, like in GNU ls. In fact append_indicator() is a rewrite from get_type_indicator() in coreutils.git commit 7326d1f1a67edf21947ae98194f98c38b6e9e527. Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/git-list-files.txt | 6 ++ builtin/ls-files.c | 31 +++ 2 files changed, 37 insertions(+) diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt index 0ef616b..22084eb 100644 --- a/Documentation/git-list-files.txt +++ b/Documentation/git-list-files.txt @@ -51,6 +51,12 @@ OPTIONS multiple file selections. See linkgit::git-ls-files[1] option `-t` for more information. +-F:: +--classify:: + Append indicator (one of `*/=>@|`, which is executable, + directory, socket, Solaris door, symlink, or fifo + respectively) to entries. + -R:: --recursive:: Equivalent of `--max-depth=-1` (infinite recursion). diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 41efdaa..28737cb 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -29,6 +29,7 @@ static int show_killed; static int show_valid_bit; static int show_tag; static int show_dirs; +static int show_indicator; static int line_terminator = '\n'; static int debug_mode; static int use_color; @@ -77,6 +78,28 @@ static void write_name(struct strbuf *sb, const char *name) quote_path_relative(name, real_prefix, sb); } +static void append_indicator(struct strbuf *sb, mode_t mode) +{ + char c = 0; + if (S_ISREG(mode)) { + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) + c = '*'; + } else if (S_ISDIR(mode)) + c = '/'; + else if (S_ISLNK(mode)) + c = '@'; + else if (S_ISFIFO(mode)) + c = '|'; + else if (S_ISSOCK(mode)) + c = '='; +#ifdef S_ISDOOR + else if (S_ISDOOR(mode)) + c = '>'; +#endif + if (c) + strbuf_addch(sb, c); +} + static void strbuf_fputs(struct strbuf *sb, const char *full_name, FILE *fp) { if (column_active(colopts) || porcelain) { @@ -99,6 +122,8 @@ static void write_dir_entry(struct strbuf *sb, const struct dir_entry *ent) color_filename(sb, ent->name, quoted.buf, st.st_mode, 1); else strbuf_addbuf(sb, "ed); + if (show_indicator && st.st_mode) + append_indicator(sb, st.st_mode); strbuf_addch(sb, line_terminator); strbuf_release("ed); } @@ -189,6 +214,8 @@ static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce) color_filename(sb, ce->name, quoted.buf, ce->ce_mode, 1); else strbuf_addbuf(sb, "ed); + if (show_indicator) + append_indicator(sb, ce->ce_mode); strbuf_addch(sb, line_terminator); strbuf_release("ed); } @@ -366,6 +393,8 @@ static void show_directories(const struct cache_entry *ce) } if (show_tag) strbuf_insert(&sb2, 0, tag_cached, strlen(tag_cached)); + if (show_indicator) + append_indicator(&sb2, S_IFDIR); last_directory = strbuf_detach(&sb, NULL); strbuf_fputs(&sb2, last_directory, NULL); strbuf_release(&sb2); @@ -701,6 +730,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) DIR_SHOW_IGNORED), OPT_BOOL('u', "unmerged", &show_unmerged, N_("show unmerged files")), + OPT_BOOL('F', "classify", &show_indicator, +N_("append indicator (one of */=>@|) to entries")), OPT__COLOR(&use_color, N_("show color")), OPT_COLUMN(0, "column", &colopts, N_("show files in columns")), OPT_SET_INT('1', NULL, &colopts, -- 2.2.0.60.gb7b3c64 -- 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 09/19] Add git-list-files, a user friendly version of ls-files and more
This is more user friendly version of ls-files: * it's automatically colored and columnized * it refreshes the index like all porcelain commands * it defaults to non-recursive behavior like ls * :(glob) is on by default so '*.c' means a.c but not a/b.c, use '**/*.c' for that. * auto pager The name 'ls' is not taken. It is left for the user to make an alias with better default options. Signed-off-by: Nguyễn Thái Ngọc Duy --- .gitignore | 1 + Documentation/config.txt | 10 + Documentation/git-list-files.txt (new) | 80 ++ Makefile | 1 + builtin/ls-files.c | 69 +++-- command-list.txt | 1 + git.c | 1 + 7 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 Documentation/git-list-files.txt diff --git a/.gitignore b/.gitignore index a052419..9727ecc 100644 --- a/.gitignore +++ b/.gitignore @@ -76,6 +76,7 @@ /git-init-db /git-interpret-trailers /git-instaweb +/git-list-files /git-log /git-ls-files /git-ls-remote diff --git a/Documentation/config.txt b/Documentation/config.txt index 2290c47..74da715 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -940,6 +940,12 @@ color.status.:: to red). The values of these variables may be specified as in color.branch.. +color.list-files:: + A boolean to enable/disable color in the output of + linkgit:git-list-files[1]. May be set to `always`, `false` (or + `never`) or `auto` (or `true`), in which case colors are used + only when the output is to a terminal. Defaults to false. + color.ls.:: Use customized color for file name colorization. If not set and the environment variable LS_COLORS is set, color settings @@ -1012,6 +1018,10 @@ column.clean:: Specify the layout when list items in `git clean -i`, which always shows files and directories in columns. See `column.ui` for details. +column.list-files:: + Specify whether to output tag listing in `git list-files` in columns. + See `column.ui` for details. + column.status:: Specify whether to output untracked files in `git status` in columns. See `column.ui` for details. diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt new file mode 100644 index 000..3039e1e --- /dev/null +++ b/Documentation/git-list-files.txt @@ -0,0 +1,80 @@ +git-list-files(1) +=== + +NAME + +git-list-files - List files + +SYNOPSIS + +[verse] +'git list-files [options] [...] + +DESCRIPTION +--- +List files (by default in current working directory) that are in the +index. Depending on the chosen options, maybe only modified files in +working tree are shown, or untracked files... + +OPTIONS +--- +-c:: +--cached:: + Show cached files (default) + +-d:: +--deleted:: + Show cached files that are deleted on working directory + +-m:: +--modified:: + Show cached files that have modification on working directory + +-o:: +--others:: + Show untracked files (and only unignored ones unless -i is + specified) + +-i:: +--ignored:: + Show only ignored files. When showing files in the index, + print only those matched by an exclude pattern. When showing + "other" files, show only those matched by an exclude pattern. + +-u:: +--unmerged:: + Show unmerged files + +--color[=]:: +--no-color:: + Color file names. The value must be `always`, `never`, or + `auto`. `--no-color` is equivalent to + `--color=never`. `--color` is equivalent to + `--color=auto`. See configuration variable `color.list-files` + for the default settings. + +--column[=]:: +--no-column:: + Display files in columns. See configuration variable column.ui + for option syntax. `--column` and `--no-column` without options + are equivalent to 'always' and 'never' respectively. + +--max-depth=:: + For each given on command line, descend at most + levels of directories. A negative value means no limit. + This option is ignored if contains active wildcards. + In other words if "a*" matches a directory named "a*", + "*" is matched literally so --max-depth is still effective. + The default is `--max-depth=0`. + +:: + Files to show. :(glob) magic is enabled and recursion disabled + by default. + +SEE ALSO + +linkgit:git-ls-files[1] + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Makefile b/Makefile index 459121d..23a0751 100644 --- a/Makefile +++ b/Makefile @@ -587,6 +587,7 @@ BUILT_INS += git-cherry-pick$X BUILT_INS += git-format-patch$X BUILT_INS += git-fsck-objects$X BUILT_INS += git-init$X +BUILT_INS += git-list-files$X BUILT_INS += git-merge-subtree$X BUILT_INS += git-show$X BUILT_INS += git-stage$X diff --git
[PATCH 11/19] list-files: add -R/--recursive short for --max-depth=-1
Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/git-list-files.txt | 4 builtin/ls-files.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt index 3039e1e..5dccbbc 100644 --- a/Documentation/git-list-files.txt +++ b/Documentation/git-list-files.txt @@ -45,6 +45,10 @@ OPTIONS --unmerged:: Show unmerged files +-R:: +--recursive:: + Equivalent of `--max-depth=-1` (infinite recursion). + --color[=]:: --no-color:: Color file names. The value must be `always`, `never`, or diff --git a/builtin/ls-files.c b/builtin/ls-files.c index f2b62b5..d0d39bd 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -594,6 +594,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) N_("show cached files that have modification on working directory")), OPT_BOOL('o', "others", &show_others, N_("show untracked files")), + OPT_SET_INT('R', "recursive", &max_depth, + N_("shortcut for --max-depth=-1"), -1), OPT_BIT('i', "ignored", &dir.flags, N_("show ignored files"), DIR_SHOW_IGNORED), -- 2.2.0.60.gb7b3c64 -- 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 12/19] list-files: add -1 short for --no-column
Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/git-list-files.txt | 3 +++ builtin/ls-files.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Documentation/git-list-files.txt b/Documentation/git-list-files.txt index 5dccbbc..725a236 100644 --- a/Documentation/git-list-files.txt +++ b/Documentation/git-list-files.txt @@ -49,6 +49,9 @@ OPTIONS --recursive:: Equivalent of `--max-depth=-1` (infinite recursion). +-1:: + Equivalent of --no-column. + --color[=]:: --no-color:: Color file names. The value must be `always`, `never`, or diff --git a/builtin/ls-files.c b/builtin/ls-files.c index d0d39bd..be9a39c 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -603,6 +603,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) N_("show unmerged files")), OPT__COLOR(&use_color, N_("show color")), OPT_COLUMN(0, "column", &colopts, N_("show files in columns")), + OPT_SET_INT('1', NULL, &colopts, + N_("shortcut for --no-column"), COL_PARSEOPT), { OPTION_INTEGER, 0, "max-depth", &max_depth, N_("depth"), N_("descend at most levels"), PARSE_OPT_NONEG, NULL, 1 }, -- 2.2.0.60.gb7b3c64 -- 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 10/19] list-files: -u does not imply showing stages
Showing full index entry information is something for ls-files only. The users of "git list-files" may just want to know what entries are not unmerged. Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/ls-files.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index f3873a8..f2b62b5 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -656,7 +656,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) } if (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed) require_work_tree = 1; - if (show_unmerged) + if (show_unmerged && !porcelain) /* * There's no point in showing unmerged unless * you also show the stage information. -- 2.2.0.60.gb7b3c64 -- 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 07/19] ls-files: add --column
Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/git-ls-files.txt | 6 ++ builtin/ls-files.c | 28 2 files changed, 34 insertions(+) diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index 148f226..99328b9 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -154,6 +154,12 @@ a space) at the start of each line: `--color=never`. `--color` is equivalent to `--color=auto`. +--column[=]:: +--no-column:: + Display files in columns. See configuration variable column.ui + for option syntax. `--column` and `--no-column` without options + are equivalent to 'always' and 'never' respectively. + \--:: Do not interpret any more arguments as options. diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 4ddd49f..79e1944 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -15,6 +15,7 @@ #include "string-list.h" #include "pathspec.h" #include "color.h" +#include "column.h" static int abbrev; static int show_deleted; @@ -29,6 +30,7 @@ static int show_valid_bit; static int line_terminator = '\n'; static int debug_mode; static int use_color; +static unsigned int colopts; static const char *prefix; static int max_prefix_len; @@ -39,6 +41,7 @@ static char *ps_matched; static const char *with_tree; static int exc_given; static int exclude_args; +static struct string_list output = STRING_LIST_INIT_NODUP; static const char *tag_cached = ""; static const char *tag_unmerged = ""; @@ -66,6 +69,10 @@ static void write_name(struct strbuf *sb, const char *name) static void strbuf_fputs(struct strbuf *sb, FILE *fp) { + if (column_active(colopts)) { + string_list_append(&output, strbuf_detach(sb, NULL)); + return; + } fwrite(sb->buf, sb->len, 1, fp); } @@ -552,6 +559,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) OPT_STRING(0, "with-tree", &with_tree, N_("tree-ish"), N_("pretend that paths removed since are still present")), OPT__COLOR(&use_color, N_("show color")), + OPT_COLUMN(0, "column", &colopts, N_("show files in columns")), OPT__ABBREV(&abbrev), OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")), OPT_END() @@ -596,6 +604,18 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (dir.exclude_per_dir) exc_given = 1; + finalize_colopts(&colopts, -1); + if (explicitly_enable_column(colopts)) { + if (!line_terminator) + die(_("--column and -z are incompatible")); + if (show_resolve_undo) + die(_("--column and --resolve-undo are incompatible")); + if (debug_mode) + die(_("--column and --debug are incompatible")); + } + if (column_active(colopts)) + line_terminator = 0; + if (require_work_tree && !is_inside_work_tree()) setup_work_tree(); @@ -638,6 +658,14 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (show_resolve_undo) show_ru_info(); + if (column_active(colopts)) { + struct column_options copts; + memset(&copts, 0, sizeof(copts)); + copts.padding = 2; + print_columns(&output, colopts, &copts); + string_list_clear(&output, 0); + } + if (ps_matched) { int bad; bad = report_path_error(ps_matched, &pathspec, prefix); -- 2.2.0.60.gb7b3c64 -- 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 08/19] ls-files: support --max-depth
Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/git-ls-files.txt | 7 +++ builtin/ls-files.c | 7 +++ 2 files changed, 14 insertions(+) diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index 99328b9..3d921eb 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -160,6 +160,13 @@ a space) at the start of each line: for option syntax. `--column` and `--no-column` without options are equivalent to 'always' and 'never' respectively. +--max-depth=:: + For each given on command line, descend at most + levels of directories. A negative value means no limit (default). + This option is ignored if contains active wildcards. + In other words if "a*" matches a directory named "a*", + "*" is matched literally so --max-depth is still effective. + \--:: Do not interpret any more arguments as options. diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 79e1944..40fe0f2 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -503,6 +503,7 @@ static int option_parse_exclude_standard(const struct option *opt, int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) { int require_work_tree = 0, show_tag = 0, i; + int max_depth = -1; const char *max_prefix; struct dir_struct dir; struct exclude_list *el; @@ -560,6 +561,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) N_("pretend that paths removed since are still present")), OPT__COLOR(&use_color, N_("show color")), OPT_COLUMN(0, "column", &colopts, N_("show files in columns")), + { OPTION_INTEGER, 0, "max-depth", &max_depth, N_("depth"), + N_("descend at most levels"), PARSE_OPT_NONEG, + NULL, 1 }, OPT__ABBREV(&abbrev), OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")), OPT_END() @@ -624,8 +628,11 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD | + (max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0) | PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP, prefix, argv); + pathspec.max_depth = max_depth; + pathspec.recursive = 1; /* Find common prefix for all pathspec's */ max_prefix = common_prefix(&pathspec); -- 2.2.0.60.gb7b3c64 -- 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 06/19] ls-files: add --color to highlight file names
Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/git-ls-files.txt | 7 +++ builtin/ls-files.c | 38 +++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index e26f01f..148f226 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -147,6 +147,13 @@ a space) at the start of each line: possible for manual inspection; the exact format may change at any time. +--color[=]:: +--no-color:: + Color file names. The value must be `always`, `never`, or + `auto`. `--no-color` is equivalent to + `--color=never`. `--color` is equivalent to + `--color=auto`. + \--:: Do not interpret any more arguments as options. diff --git a/builtin/ls-files.c b/builtin/ls-files.c index f20c0fd..4ddd49f 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -14,6 +14,7 @@ #include "resolve-undo.h" #include "string-list.h" #include "pathspec.h" +#include "color.h" static int abbrev; static int show_deleted; @@ -27,6 +28,7 @@ static int show_killed; static int show_valid_bit; static int line_terminator = '\n'; static int debug_mode; +static int use_color; static const char *prefix; static int max_prefix_len; @@ -60,7 +62,6 @@ static void write_name(struct strbuf *sb, const char *name) strbuf_release(&sb2); } else quote_path_relative(name, real_prefix, sb); - strbuf_addch(sb, line_terminator); } static void strbuf_fputs(struct strbuf *sb, FILE *fp) @@ -68,6 +69,21 @@ static void strbuf_fputs(struct strbuf *sb, FILE *fp) fwrite(sb->buf, sb->len, 1, fp); } +static void write_dir_entry(struct strbuf *sb, const struct dir_entry *ent) +{ + struct strbuf quoted = STRBUF_INIT; + struct stat st; + if (stat(ent->name, &st)) + st.st_mode = 0; + write_name("ed, ent->name); + if (want_color(use_color)) + color_filename(sb, ent->name, quoted.buf, st.st_mode, 1); + else + strbuf_addbuf(sb, "ed); + strbuf_addch(sb, line_terminator); + strbuf_release("ed); +} + static void show_dir_entry(const char *tag, struct dir_entry *ent) { static struct strbuf sb = STRBUF_INIT; @@ -81,7 +97,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) strbuf_reset(&sb); strbuf_addstr(&sb, tag); - write_name(&sb, ent->name); + write_dir_entry(&sb, ent); strbuf_fputs(&sb, stdout); } @@ -146,6 +162,18 @@ static void show_killed_files(struct dir_struct *dir) } } +static void write_ce_name(struct strbuf *sb, const struct cache_entry *ce) +{ + struct strbuf quoted = STRBUF_INIT; + write_name("ed, ce->name); + if (want_color(use_color)) + color_filename(sb, ce->name, quoted.buf, ce->ce_mode, 1); + else + strbuf_addbuf(sb, "ed); + strbuf_addch(sb, line_terminator); + strbuf_release("ed); +} + static void show_ce_entry(const char *tag, const struct cache_entry *ce) { static struct strbuf sb = STRBUF_INIT; @@ -186,7 +214,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce) find_unique_abbrev(ce->sha1,abbrev), ce_stage(ce)); } - write_name(&sb, ce->name); + write_ce_name(&sb, ce); strbuf_fputs(&sb, stdout); if (debug_mode) { const struct stat_data *sd = &ce->ce_stat_data; @@ -523,6 +551,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) N_("if any is not in the index, treat this as an error")), OPT_STRING(0, "with-tree", &with_tree, N_("tree-ish"), N_("pretend that paths removed since are still present")), + OPT__COLOR(&use_color, N_("show color")), OPT__ABBREV(&abbrev), OPT_BOOL(0, "debug", &debug_mode, N_("show debugging data")), OPT_END() @@ -570,6 +599,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) if (require_work_tree && !is_inside_work_tree()) setup_work_tree(); + if (want_color(use_color)) + parse_ls_color(); + parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD | PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP, -- 2.2.0.60.gb7b3c64 -- 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 05/19] ls-files: buffer full item in strbuf before printing
Buffering so that we can manipulate the strings (e.g. coloring) further before finally printing them. Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/ls-files.c | 48 +++- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 99cee20..f20c0fd 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -47,18 +47,30 @@ static const char *tag_modified = ""; static const char *tag_skip_worktree = ""; static const char *tag_resolve_undo = ""; -static void write_name(const char *name) +static void write_name(struct strbuf *sb, const char *name) { /* * With "--full-name", prefix_len=0; this caller needs to pass * an empty string in that case (a NULL is good for ""). */ - write_name_quoted_relative(name, prefix_len ? prefix : NULL, - stdout, line_terminator); + const char *real_prefix = prefix_len ? prefix : NULL; + if (!line_terminator) { + struct strbuf sb2 = STRBUF_INIT; + strbuf_addstr(sb, relative_path(name, real_prefix, &sb2)); + strbuf_release(&sb2); + } else + quote_path_relative(name, real_prefix, sb); + strbuf_addch(sb, line_terminator); +} + +static void strbuf_fputs(struct strbuf *sb, FILE *fp) +{ + fwrite(sb->buf, sb->len, 1, fp); } static void show_dir_entry(const char *tag, struct dir_entry *ent) { + static struct strbuf sb = STRBUF_INIT; int len = max_prefix_len; if (len >= ent->len) @@ -67,8 +79,10 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) if (!dir_path_match(ent, &pathspec, len, ps_matched)) return; - fputs(tag, stdout); - write_name(ent->name); + strbuf_reset(&sb); + strbuf_addstr(&sb, tag); + write_name(&sb, ent->name); + strbuf_fputs(&sb, stdout); } static void show_other_files(struct dir_struct *dir) @@ -134,6 +148,7 @@ static void show_killed_files(struct dir_struct *dir) static void show_ce_entry(const char *tag, const struct cache_entry *ce) { + static struct strbuf sb = STRBUF_INIT; int len = max_prefix_len; if (len >= ce_namelen(ce)) @@ -161,16 +176,18 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce) tag = alttag; } + strbuf_reset(&sb); if (!show_stage) { - fputs(tag, stdout); + strbuf_addstr(&sb, tag); } else { - printf("%s%06o %s %d\t", - tag, - ce->ce_mode, - find_unique_abbrev(ce->sha1,abbrev), - ce_stage(ce)); + strbuf_addf(&sb, "%s%06o %s %d\t", + tag, + ce->ce_mode, + find_unique_abbrev(ce->sha1,abbrev), + ce_stage(ce)); } - write_name(ce->name); + write_name(&sb, ce->name); + strbuf_fputs(&sb, stdout); if (debug_mode) { const struct stat_data *sd = &ce->ce_stat_data; @@ -206,7 +223,12 @@ static void show_ru_info(void) printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i], find_unique_abbrev(ui->sha1[i], abbrev), i + 1); - write_name(path); + /* +* With "--full-name", prefix_len=0; this caller needs to pass +* an empty string in that case (a NULL is good for ""). +*/ + write_name_quoted_relative(path, prefix_len ? prefix : NULL, + stdout, line_terminator); } } } -- 2.2.0.60.gb7b3c64 -- 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 01/19] ls_colors.c: add $LS_COLORS parsing code
Reusing color settings from $LS_COLORS could give a native look and feel on file coloring. This code is basically from coreutils.git [1], rewritten to fit Git. As this is from GNU ls, the environment variable CLICOLOR is not tested. It is to be decided later whether we should ignore $LS_COLORS if $CLICOLOR is not set on Mac or FreeBSD. [1] commit 7326d1f1a67edf21947ae98194f98c38b6e9e527 file src/ls.c. This is the last GPL-2 commit before coreutils turns to GPL-3. Signed-off-by: Nguyễn Thái Ngọc Duy --- Makefile | 1 + color.h | 8 ++ ls_colors.c (new) | 398 ++ 3 files changed, 407 insertions(+) create mode 100644 ls_colors.c diff --git a/Makefile b/Makefile index 827006b..459121d 100644 --- a/Makefile +++ b/Makefile @@ -703,6 +703,7 @@ LIB_OBJS += list-objects.o LIB_OBJS += ll-merge.o LIB_OBJS += lockfile.o LIB_OBJS += log-tree.o +LIB_OBJS += ls_colors.o LIB_OBJS += mailmap.o LIB_OBJS += match-trees.o LIB_OBJS += merge.o diff --git a/color.h b/color.h index f5beab1..3eaa5bd 100644 --- a/color.h +++ b/color.h @@ -45,6 +45,12 @@ struct strbuf; #define GIT_COLOR_BG_MAGENTA "\033[45m" #define GIT_COLOR_BG_CYAN "\033[46m" +#define GIT_COLOR_WHITE_ON_RED"\033[37;41m" +#define GIT_COLOR_WHITE_ON_BLUE "\033[37;44m" +#define GIT_COLOR_BLACK_ON_YELLOW "\033[30;43m" +#define GIT_COLOR_BLUE_ON_GREEN "\033[34;42m" +#define GIT_COLOR_BLACK_ON_GREEN "\033[30;42m" + /* A special value meaning "no color selected" */ #define GIT_COLOR_NIL "NIL" @@ -87,4 +93,6 @@ void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb); int color_is_nil(const char *color); +void parse_ls_color(void); + #endif /* COLOR_H */ diff --git a/ls_colors.c b/ls_colors.c new file mode 100644 index 000..eb944f4 --- /dev/null +++ b/ls_colors.c @@ -0,0 +1,398 @@ +#include "cache.h" +#include "color.h" + +enum color_ls { + LS_LC, /* left, unused */ + LS_RC, /* right, unused */ + LS_EC, /* end color, unused */ + LS_RS, /* reset */ + LS_NO, /* normal */ + LS_FL, /* file, default */ + LS_DI, /* directory */ + LS_LN, /* symlink */ + + LS_PI, /* pipe */ + LS_SO, /* socket */ + LS_BD, /* block device */ + LS_CD, /* char device */ + LS_MI, /* missing file */ + LS_OR, /* orphaned symlink */ + LS_EX, /* executable */ + LS_DO, /* Solaris door */ + + LS_SU, /* setuid */ + LS_SG, /* setgid */ + LS_ST, /* sticky */ + LS_OW, /* other-writable */ + LS_TW, /* ow with sticky */ + LS_CA, /* cap */ + LS_MH, /* multi hardlink */ + LS_CL, /* clear end of line */ + + MAX_LS +}; + +static char ls_colors[MAX_LS][COLOR_MAXLEN] = { + "", + "", + "", + GIT_COLOR_RESET, + GIT_COLOR_NORMAL, + GIT_COLOR_NORMAL, + GIT_COLOR_BOLD_BLUE, + GIT_COLOR_BOLD_CYAN, + + GIT_COLOR_YELLOW, + GIT_COLOR_BOLD_MAGENTA, + GIT_COLOR_BOLD_YELLOW, + GIT_COLOR_BOLD_YELLOW, + GIT_COLOR_NORMAL, + GIT_COLOR_NORMAL, + GIT_COLOR_BOLD_GREEN, + GIT_COLOR_BOLD_MAGENTA, + + GIT_COLOR_WHITE_ON_RED, + GIT_COLOR_BLACK_ON_YELLOW, + GIT_COLOR_WHITE_ON_BLUE, + GIT_COLOR_BLUE_ON_GREEN, + GIT_COLOR_BLACK_ON_GREEN, + "", + "", + "" +}; + +static const char *const indicator_name[] = { + "lc", "rc", "ec", "rs", "no", "fi", "di", "ln", + "pi", "so", "bd", "cd", "mi", "or", "ex", "do", + "su", "sg", "st", "ow", "tw", "ca", "mh", "cl", + NULL +}; + +struct bin_str { + size_t len; /* Number of bytes */ + const char *string; /* Pointer to the same */ +}; + +struct color_ext_type { + struct bin_str ext; /* The extension we're looking for */ + struct bin_str seq; /* The sequence to output when we do */ + struct color_ext_type *next;/* Next in list */ +}; + +static struct color_ext_type *color_ext_list = NULL; + +/* + * When true, in a color listing, color each symlink name according to the + * type of file it points to. Otherwise, color them according to the `ln' + * directive in LS_COLORS. Dangling (orphan) symlinks are treated specially, + * regardless. This is set when `ln=target' appears in LS_COLORS. + */ +static int color_symlink_as_referent; + +/* + * Parse a string as part of the LS_COLORS variable; this may involve + * decoding all kinds of escape characters. If equal
[PATCH 04/19] ls_colors.c: highlight submodules like directories
Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/config.txt | 3 ++- ls_colors.c | 8 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 2090866..2290c47 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -944,7 +944,8 @@ color.ls.:: Use customized color for file name colorization. If not set and the environment variable LS_COLORS is set, color settings from $LS_COLORS are used. `` can be `normal`, `file`, - `directory`, `symlink`, `fifo`, `socket`, `block`, `char`, + `directory`, `submodule`, + `symlink`, `fifo`, `socket`, `block`, `char`, `missing`, `orphan`, `executable`, `door`, `setuid`, `setgid`, `sticky`, `otherwritable`, `stickyotherwritable`, `cap`, `multihardlink`. The values of these variables may be diff --git a/ls_colors.c b/ls_colors.c index a58da9e..e62e74b 100644 --- a/ls_colors.c +++ b/ls_colors.c @@ -29,6 +29,8 @@ enum color_ls { LS_MH, /* multi hardlink */ LS_CL, /* clear end of line */ + LS_SUBMODULE, + MAX_LS }; @@ -58,7 +60,8 @@ static char ls_colors[MAX_LS][COLOR_MAXLEN] = { GIT_COLOR_BLACK_ON_GREEN, "", "", - "" + "", + GIT_COLOR_BOLD_BLUE }; static const char *const indicator_name[] = { @@ -73,6 +76,7 @@ static const char* const config_name[] = { "fifo", "socket", "block", "char", "missing", "orphan", "executable", "door", "setuid", "setgid", "sticky", "otherwritable", "stickyotherwritable", "cap", "multihardlink", "", + "submodule", NULL }; @@ -448,6 +452,8 @@ void color_filename(struct strbuf *sb, const char *name, type = LS_DI; } else if (S_ISLNK (mode)) type = (!linkok && *ls_colors[LS_OR]) ? LS_OR : LS_LN; + else if (S_ISGITLINK(mode)) + type = LS_SUBMODULE; else if (S_ISFIFO (mode)) type = LS_PI; else if (S_ISSOCK (mode)) -- 2.2.0.60.gb7b3c64 -- 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 03/19] ls_colors.c: add a function to color a file name
The new function is based on print_color_indicator() from commit 7326d1f1a67edf21947ae98194f98c38b6e9e527 in coreutils.git. Signed-off-by: Nguyễn Thái Ngọc Duy --- color.h | 2 ++ ls_colors.c | 66 + 2 files changed, 68 insertions(+) diff --git a/color.h b/color.h index 3eaa5bd..b6904a3 100644 --- a/color.h +++ b/color.h @@ -94,5 +94,7 @@ void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb); int color_is_nil(const char *color); void parse_ls_color(void); +void color_filename(struct strbuf *sb, const char *name, + const char *display_name, mode_t mode, int linkok); #endif /* COLOR_H */ diff --git a/ls_colors.c b/ls_colors.c index 3e35ffe..a58da9e 100644 --- a/ls_colors.c +++ b/ls_colors.c @@ -422,3 +422,69 @@ void parse_ls_color(void) color_symlink_as_referent = 1; git_config(ls_colors_config, NULL); } + +void color_filename(struct strbuf *sb, const char *name, + const char *display_name, mode_t mode, int linkok) +{ + int type; + struct color_ext_type *ext; /* Color extension */ + + if (S_ISREG (mode)) { + type = LS_FL; + if ((mode & S_ISUID) != 0) + type = LS_SU; + else if ((mode & S_ISGID) != 0) + type = LS_SG; + else if ((mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0) + type = LS_EX; + } else if (S_ISDIR (mode)) { + if ((mode & S_ISVTX) && (mode & S_IWOTH)) + type = LS_TW; + else if ((mode & S_IWOTH) != 0) + type = LS_OW; + else if ((mode & S_ISVTX) != 0) + type = LS_ST; + else + type = LS_DI; + } else if (S_ISLNK (mode)) + type = (!linkok && *ls_colors[LS_OR]) ? LS_OR : LS_LN; + else if (S_ISFIFO (mode)) + type = LS_PI; + else if (S_ISSOCK (mode)) + type = LS_SO; + else if (S_ISBLK (mode)) + type = LS_BD; + else if (S_ISCHR (mode)) + type = LS_CD; +#ifdef S_ISDOOR + else if (S_ISDOOR (mode)) + type = LS_DO; +#endif + else + /* Classify a file of some other type as C_ORPHAN. */ + type = LS_OR; + + /* Check the file's suffix only if still classified as C_FILE. */ + ext = NULL; + if (type == LS_FL) { + /* Test if NAME has a recognized suffix. */ + size_t len = strlen(name); + const char *p = name + len; /* Pointer to final \0. */ + for (ext = color_ext_list; ext != NULL; ext = ext->next) { + if (ext->ext.len <= len && + !strncmp(p - ext->ext.len, ext->ext.string, ext->ext.len)) + break; + } + } + + if (display_name) + name = display_name; + if (ext) + strbuf_addf(sb, "\033[%.*sm%s%s", + (int)ext->seq.len, ext->seq.string, + name, GIT_COLOR_RESET); + else if (*ls_colors[type]) + strbuf_addf(sb, "%s%s%s", ls_colors[type], name, GIT_COLOR_RESET); + else + strbuf_addstr(sb, name); +} -- 2.2.0.60.gb7b3c64 -- 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 00/19] Add git-list-files
This is something else that's been sitting in my tree for a while now. It adds "git list-files", intended to be aliased as "ls" with your favourite display options. As you can guess it's a friendlier version (and pretty close to GNU ls) of ls-files. On one hand, I think this is a nice addition. On the other hand, code bloat. Last version on the list is http://thread.gmane.org/gmane.comp.version-control.git/244530/focus=245464 Nguyễn Thái Ngọc Duy (19): ls_colors.c: add $LS_COLORS parsing code ls_colors.c: parse color.ls.* from config file ls_colors.c: add a function to color a file name ls_colors.c: highlight submodules like directories ls-files: buffer full item in strbuf before printing ls-files: add --color to highlight file names ls-files: add --column ls-files: support --max-depth Add git-list-files, a user friendly version of ls-files and more list-files: -u does not imply showing stages list-files: add -R/--recursive short for --max-depth=-1 list-files: add -1 short for --no-column list-files: add -t back list-files: sort output and remove duplicates list-files: do not show duplicate cached entries list-files: show directories as well as files list-files: add -F/--classify list-files -F: show submodules with the new indicator '&' list-files: -M aka diff-cached .gitignore | 1 + Documentation/config.txt | 22 ++ Documentation/git-list-files.txt (new) | 99 +++ Documentation/git-ls-files.txt | 20 ++ Makefile | 2 + builtin/ls-files.c | 415 --- color.h| 10 + command-list.txt | 1 + git.c | 1 + ls_colors.c (new) | 496 + 10 files changed, 1034 insertions(+), 33 deletions(-) create mode 100644 Documentation/git-list-files.txt create mode 100644 ls_colors.c -- 2.2.0.60.gb7b3c64 -- 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 02/19] ls_colors.c: parse color.ls.* from config file
This is the second (and preferred) source for color information. This will override $LS_COLORS. Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/config.txt | 11 +++ ls_colors.c | 26 ++ 2 files changed, 37 insertions(+) diff --git a/Documentation/config.txt b/Documentation/config.txt index 9220725..2090866 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -940,6 +940,17 @@ color.status.:: to red). The values of these variables may be specified as in color.branch.. +color.ls.:: + Use customized color for file name colorization. If not set + and the environment variable LS_COLORS is set, color settings + from $LS_COLORS are used. `` can be `normal`, `file`, + `directory`, `symlink`, `fifo`, `socket`, `block`, `char`, + `missing`, `orphan`, `executable`, `door`, `setuid`, `setgid`, + `sticky`, `otherwritable`, `stickyotherwritable`, `cap`, + `multihardlink`. The values of these variables may be + specified as in color.branch.. + + color.ui:: This variable determines the default value for variables such as `color.diff` and `color.grep` that control the use of color diff --git a/ls_colors.c b/ls_colors.c index eb944f4..3e35ffe 100644 --- a/ls_colors.c +++ b/ls_colors.c @@ -68,6 +68,14 @@ static const char *const indicator_name[] = { NULL }; +static const char* const config_name[] = { + "", "", "", "", "normal", "file", "directory", "symlink", + "fifo", "socket", "block", "char", "missing", "orphan", "executable", + "door", "setuid", "setgid", "sticky", "otherwritable", + "stickyotherwritable", "cap", "multihardlink", "", + NULL +}; + struct bin_str { size_t len; /* Number of bytes */ const char *string; /* Pointer to the same */ @@ -285,6 +293,23 @@ static int get_funky_string(char **dest, const char **src, int equals_end, return state != ST_ERROR; } +static int ls_colors_config(const char *var, const char *value, void *cb) +{ + int slot; + if (!starts_with(var, "color.ls.")) + return 0; + var += 9; + for (slot = 0; config_name[slot]; slot++) + if (!strcasecmp(var, config_name[slot])) + break; + if (!config_name[slot]) + return 0; + if (!value) + return config_error_nonbool(var); + color_parse(value, ls_colors[slot]); + return 0; +} + void parse_ls_color(void) { const char *p; /* Pointer to character being parsed */ @@ -395,4 +420,5 @@ void parse_ls_color(void) if (!strcmp(ls_colors[LS_LN], "target")) color_symlink_as_referent = 1; + git_config(ls_colors_config, NULL); } -- 2.2.0.60.gb7b3c64 -- 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: What's cooking in git.git (Nov 2014, #04; Wed, 26)
On Fri, Nov 28, 2014 at 1:39 AM, brian m. carlson wrote: > On Wed, Nov 26, 2014 at 03:09:45PM -0800, Junio C Hamano wrote: >> * nd/untracked-cache (2014-10-27) 19 commits >> - t7063: tests for untracked cache >> - update-index: test the system before enabling untracked cache >> - update-index: manually enable or disable untracked cache >> - status: enable untracked cache >> - untracked cache: mark index dirty if untracked cache is updated >> - untracked cache: print stats with $GIT_TRACE_UNTRACKED_STATS >> - untracked cache: avoid racy timestamps >> - read-cache.c: split racy stat test to a separate function >> - untracked cache: invalidate at index addition or removal >> - untracked cache: load from UNTR index extension >> - untracked cache: save to an index extension >> - untracked cache: don't open non-existent .gitignore >> - untracked cache: mark what dirs should be recursed/saved >> - untracked cache: record/validate dir mtime and reuse cached output >> - untracked cache: make a wrapper around {open,read,close}dir() >> - untracked cache: invalidate dirs recursively if .gitignore changes >> - untracked cache: initial untracked cache validation >> - untracked cache: record .gitignore information and dir hierarchy >> - dir.c: optionally compute sha-1 of a .gitignore file > > You didn't comment on the status of this branch, and I'm interested. I'm not Junio :) but I think the core changes are done. I wanted to actually add watchman support on top of untracked cache as well to see if it needs any more changes. I think I can see how it could be done now (not easy, but not terribly hard). I'm going to resend soon to fix some minor bugs (in a reroll that Junio has not picked up) and change file format to be more compact. -- Duy -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 34/34] git-common-dir: make "modules/" per-working-directory directory
From: Max Kirillov Each working directory of main repository has its own working directory of submodule, and in most cases they should be checked out to different revisions. So they should be separated. It looks logical to make submodule instances in different working directories to reuse the submodule directory in the common dir of the main repository, and probably this is how "checkout --to" should initialize them called on the main repository, but they also should work fine being completely separated clones. Testfile t7410-submodule-checkout-to.sh demostrates the behavior. Signed-off-by: Max Kirillov Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/gitrepository-layout.txt| 4 +-- path.c| 2 +- t/t7410-submodule-checkout-to.sh (new +x) | 50 +++ 3 files changed, 52 insertions(+), 4 deletions(-) create mode 100755 t/t7410-submodule-checkout-to.sh diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt index 2b30a92..7173b38 100644 --- a/Documentation/gitrepository-layout.txt +++ b/Documentation/gitrepository-layout.txt @@ -248,9 +248,7 @@ commondir:: incomplete without the repository pointed by "commondir". modules:: - Contains the git-repositories of the submodules. This - directory is ignored if $GIT_COMMON_DIR is set and - "$GIT_COMMON_DIR/modules" will be used instead. + Contains the git-repositories of the submodules. worktrees:: Contains worktree specific information of linked diff --git a/path.c b/path.c index 35d498e..a5c51a3 100644 --- a/path.c +++ b/path.c @@ -92,7 +92,7 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir) } static const char *common_list[] = { - "/branches", "/hooks", "/info", "!/logs", "/lost-found", "/modules", + "/branches", "/hooks", "/info", "!/logs", "/lost-found", "/objects", "/refs", "/remotes", "/worktrees", "/rr-cache", "/svn", "config", "!gc.pid", "packed-refs", "shallow", NULL diff --git a/t/t7410-submodule-checkout-to.sh b/t/t7410-submodule-checkout-to.sh new file mode 100755 index 000..8f30aed --- /dev/null +++ b/t/t7410-submodule-checkout-to.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +test_description='Combination of submodules and multiple workdirs' + +. ./test-lib.sh + +base_path=$(pwd -P) + +test_expect_success 'setup: make origin' \ +'mkdir -p origin/sub && ( cd origin/sub && git init && + echo file1 >file1 && + git add file1 && + git commit -m file1 ) && +mkdir -p origin/main && ( cd origin/main && git init && + git submodule add ../sub && + git commit -m "add sub" ) && +( cd origin/sub && + echo file1updated >file1 && + git add file1 && + git commit -m "file1 updated" ) && +( cd origin/main/sub && git pull ) && +( cd origin/main && + git add sub && + git commit -m "sub updated" )' + +test_expect_success 'setup: clone' \ +'mkdir clone && ( cd clone && + git clone --recursive "$base_path/origin/main")' + +rev1_hash_main=$(git --git-dir=origin/main/.git show --pretty=format:%h -q "HEAD~1") +rev1_hash_sub=$(git --git-dir=origin/sub/.git show --pretty=format:%h -q "HEAD~1") + +test_expect_success 'checkout main' \ +'mkdir default_checkout && +(cd clone/main && + git checkout --to "$base_path/default_checkout/main" "$rev1_hash_main")' + +test_expect_failure 'can see submodule diffs just after checkout' \ +'(cd default_checkout/main && git diff --submodule master"^!" | grep "file1 updated")' + +test_expect_success 'checkout main and initialize independed clones' \ +'mkdir fully_cloned_submodule && +(cd clone/main && + git checkout --to "$base_path/fully_cloned_submodule/main" "$rev1_hash_main") && +(cd fully_cloned_submodule/main && git submodule update)' + +test_expect_success 'can see submodule diffs after independed cloning' \ +'(cd fully_cloned_submodule/main && git diff --submodule master"^!" | grep "file1 updated")' + +test_done -- 2.1.0.rc0.78.gc0d8480 -- 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 25/34] checkout: clean up half-prepared directories in --to mode
Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/checkout.c | 48 t/t2025-checkout-to.sh | 6 ++ 2 files changed, 54 insertions(+) diff --git a/builtin/checkout.c b/builtin/checkout.c index 01a28b4..5dfdbda 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -20,6 +20,7 @@ #include "resolve-undo.h" #include "submodule.h" #include "argv-array.h" +#include "sigchain.h" static const char * const checkout_usage[] = { N_("git checkout [options] "), @@ -823,6 +824,35 @@ static int switch_branches(const struct checkout_opts *opts, return ret || writeout_error; } +static char *junk_work_tree; +static char *junk_git_dir; +static int is_junk; +static pid_t junk_pid; + +static void remove_junk(void) +{ + struct strbuf sb = STRBUF_INIT; + if (!is_junk || getpid() != junk_pid) + return; + if (junk_git_dir) { + strbuf_addstr(&sb, junk_git_dir); + remove_dir_recursively(&sb, 0); + strbuf_reset(&sb); + } + if (junk_work_tree) { + strbuf_addstr(&sb, junk_work_tree); + remove_dir_recursively(&sb, 0); + } + strbuf_release(&sb); +} + +static void remove_junk_on_signal(int signo) +{ + remove_junk(); + sigchain_pop(signo); + raise(signo); +} + static int prepare_linked_checkout(const struct checkout_opts *opts, struct branch_info *new) { @@ -859,8 +889,15 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, strbuf_addf(&sb_repo, "%d", counter); } name = strrchr(sb_repo.buf, '/') + 1; + + junk_pid = getpid(); + atexit(remove_junk); + sigchain_push_common(remove_junk_on_signal); + if (mkdir(sb_repo.buf, 0777)) die_errno(_("could not create directory of '%s'"), sb_repo.buf); + junk_git_dir = xstrdup(sb_repo.buf); + is_junk = 1; /* * lock the incomplete repo so prune won't delete it, unlock @@ -873,6 +910,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, if (safe_create_leading_directories_const(sb_git.buf)) die_errno(_("could not create leading directories of '%s'"), sb_git.buf); + junk_work_tree = xstrdup(path); strbuf_reset(&sb); strbuf_addf(&sb, "%s/gitdir", sb_repo.buf); @@ -902,9 +940,19 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, cp.git_cmd = 1; cp.argv = opts->saved_argv; ret = run_command(&cp); + if (!ret) { + is_junk = 0; + free(junk_work_tree); + free(junk_git_dir); + junk_work_tree = NULL; + junk_git_dir = NULL; + } strbuf_reset(&sb); strbuf_addf(&sb, "%s/locked", sb_repo.buf); unlink_or_warn(sb.buf); + strbuf_release(&sb); + strbuf_release(&sb_repo); + strbuf_release(&sb_git); return ret; } diff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh index edd3404..e2db078 100755 --- a/t/t2025-checkout-to.sh +++ b/t/t2025-checkout-to.sh @@ -17,6 +17,12 @@ test_expect_success 'checkout --to an existing worktree' ' test_must_fail git checkout --detach --to existing master ' +test_expect_success 'checkout --to refuses to checkout locked branch' ' + test_must_fail git checkout --to zere master && + ! test -d zere && + ! test -d .git/worktrees/zere +' + test_expect_success 'checkout --to a new worktree' ' git rev-parse HEAD >expect && git checkout --detach --to here master && -- 2.1.0.rc0.78.gc0d8480 -- 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 23/34] prune: strategies for linked checkouts
(alias R=$GIT_COMMON_DIR/worktrees/) - linked checkouts are supposed to keep its location in $R/gitdir up to date. The use case is auto fixup after a manual checkout move. - linked checkouts are supposed to update mtime of $R/gitdir. If $R/gitdir's mtime is older than a limit, and it points to nowhere, worktrees/ is to be pruned. - If $R/locked exists, worktrees/ is not supposed to be pruned. If $R/locked exists and $R/gitdir's mtime is older than a really long limit, warn about old unused repo. - "git checkout --to" is supposed to make a hard link named $R/link pointing to the .git file on supported file systems to help detect the user manually deleting the checkout. If $R/link exists and its link count is greated than 1, the repo is kept. Helped-by: Marc Branchaud Helped-by: Eric Sunshine Helped-by: Johannes Sixt Signed-off-by: Marc Branchaud Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/git-checkout.txt | 20 +++ Documentation/git-prune.txt| 3 + Documentation/gitrepository-layout.txt | 19 ++ builtin/checkout.c | 19 +- builtin/prune.c| 95 ++ setup.c| 13 t/t2026-prune-linked-checkouts.sh (new +x) | 84 ++ 7 files changed, 251 insertions(+), 2 deletions(-) create mode 100755 t/t2026-prune-linked-checkouts.sh diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index c101575..35675da 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -434,6 +434,26 @@ thumb is do not make any assumption about whether a path belongs to $GIT_DIR or $GIT_COMMON_DIR when you need to directly access something inside $GIT_DIR. Use `git rev-parse --git-path` to get the final path. +When you are done with a linked working tree you can simply delete it. +You can clean up any stale $GIT_DIR/worktrees entries via `git prune +--worktrees` in the main or any linked working tree. + +If you move a linked working directory to another file system, or +within a file system that does not support hard links, you need to run +at least one git command inside the linked working directory +(e.g. `git status`) in order to update its entry in $GIT_DIR/worktrees +so that it does not get automatically removed. + +To prevent `git prune --worktrees` from deleting a $GIT_DIR/worktrees +entry (which can be useful in some situations, such as when the +entry's working tree is stored on a portable device), add a file named +'locked' to the entry's directory. The file contains the reason in +plain text. For example, if a linked working tree's `.git` file points +to `/path/main/.git/worktrees/test-next` then a file named +`/path/main/.git/worktrees/test-next/locked` will prevent the +`test-next` entry from being pruned. See +linkgit:gitrepository-layout[5] for details. + EXAMPLES diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt index 7a493c8..1cf3bed 100644 --- a/Documentation/git-prune.txt +++ b/Documentation/git-prune.txt @@ -48,6 +48,9 @@ OPTIONS --expire :: Only expire loose objects older than . +--worktrees:: + Prune dead working tree information in $GIT_DIR/worktrees. + ...:: In addition to objects reachable from any of our references, keep objects diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt index 8228450..2b30a92 100644 --- a/Documentation/gitrepository-layout.txt +++ b/Documentation/gitrepository-layout.txt @@ -259,6 +259,25 @@ worktrees:: $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/worktrees" will be used instead. +worktrees//gitdir:: + A text file containing the absolute path back to the .git file + that points to here. This is used to check if the linked + repository has been manually removed and there is no need to + keep this directory any more. mtime of this file should be + updated every time the linked repository is accessed. + +worktrees//locked:: + If this file exists, the linked repository may be on a + portable device and not available. It does not mean that the + linked repository is gone and `worktrees/` could be + removed. The file's content contains a reason string on why + the repository is locked. + +worktrees//link:: + If this file exists, it is a hard link to the linked .git + file. It is used to detect if the linked repository is + manually removed. + SEE ALSO linkgit:git-init[1], diff --git a/builtin/checkout.c b/builtin/checkout.c index 797e14d..645135a 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -826,7 +826,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, const char *path = opts->new_worktree, *name; struct stat st; struct child_process cp;
[PATCH 33/34] checkout: do not fail if target is an empty directory
From: Max Kirillov Non-recursive checkout creates empty directpries in place of submodules. If then I try to "checkout --to" submodules there, it refuses to do so, because directory already exists. Fix by allowing checking out to empty directory. Add test and modify the existing one so that it uses non-empty directory. Signed-off-by: Max Kirillov Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/checkout.c | 2 +- t/t2025-checkout-to.sh | 7 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 8c5276c..953b763 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -865,7 +865,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, if (!new->commit) die(_("no branch specified")); - if (file_exists(path)) + if (file_exists(path) && !is_empty_dir(path)) die(_("'%s' already exists"), path); len = strlen(path); diff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh index eddd325..915b506 100755 --- a/t/t2025-checkout-to.sh +++ b/t/t2025-checkout-to.sh @@ -13,10 +13,15 @@ test_expect_success 'checkout --to not updating paths' ' ' test_expect_success 'checkout --to an existing worktree' ' - mkdir existing && + mkdir -p existing/subtree && test_must_fail git checkout --detach --to existing master ' +test_expect_success 'checkout --to an existing empty worktree' ' + mkdir existing_empty && + git checkout --detach --to existing_empty master +' + test_expect_success 'checkout --to refuses to checkout locked branch' ' test_must_fail git checkout --to zere master && ! test -d zere && -- 2.1.0.rc0.78.gc0d8480 -- 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 28/34] gc: support prune --worktrees
Helped-by: Marc Branchaud Signed-off-by: Marc Branchaud Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/config.txt | 7 +++ Documentation/git-checkout.txt | 11 +++ builtin/gc.c | 10 ++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index e1623ec..2700a1b 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1229,6 +1229,13 @@ gc.pruneexpire:: "now" may be used to disable this grace period and always prune unreachable objects immediately. +gc.pruneworktreesexpire:: + When 'git gc' is run, it will call + 'prune --worktrees --expire 3.months.ago'. + Override the grace period with this config variable. The value + "now" may be used to disable the grace period and prune + $GIT_DIR/worktrees immediately. + gc.reflogexpire:: gc..reflogexpire:: 'git reflog expire' removes reflog entries older than diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 35675da..0c13825 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -435,8 +435,11 @@ $GIT_DIR or $GIT_COMMON_DIR when you need to directly access something inside $GIT_DIR. Use `git rev-parse --git-path` to get the final path. When you are done with a linked working tree you can simply delete it. -You can clean up any stale $GIT_DIR/worktrees entries via `git prune ---worktrees` in the main or any linked working tree. +The working tree's entry in the repository's $GIT_DIR/worktrees +directory will eventually be removed automatically (see +`gc.pruneworktreesexpire` in linkgit::git-config[1]), or you can run +`git prune --worktrees` in the main or any linked working tree to +clean up any stale entries in $GIT_DIR/worktrees. If you move a linked working directory to another file system, or within a file system that does not support hard links, you need to run @@ -444,8 +447,8 @@ at least one git command inside the linked working directory (e.g. `git status`) in order to update its entry in $GIT_DIR/worktrees so that it does not get automatically removed. -To prevent `git prune --worktrees` from deleting a $GIT_DIR/worktrees -entry (which can be useful in some situations, such as when the +To prevent a $GIT_DIR/worktrees entry from from being pruned (which +can be useful in some situations, such as when the entry's working tree is stored on a portable device), add a file named 'locked' to the entry's directory. The file contains the reason in plain text. For example, if a linked working tree's `.git` file points diff --git a/builtin/gc.c b/builtin/gc.c index 0728650..fa87ad3 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -33,11 +33,13 @@ static int gc_auto_threshold = 6700; static int gc_auto_pack_limit = 50; static int detach_auto = 1; static const char *prune_expire = "2.weeks.ago"; +static const char *prune_worktrees_expire = "3.months.ago"; static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT; static struct argv_array reflog = ARGV_ARRAY_INIT; static struct argv_array repack = ARGV_ARRAY_INIT; static struct argv_array prune = ARGV_ARRAY_INIT; +static struct argv_array prune_worktrees = ARGV_ARRAY_INIT; static struct argv_array rerere = ARGV_ARRAY_INIT; static char *pidfile; @@ -83,6 +85,7 @@ static void gc_config(void) git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit); git_config_get_bool("gc.autodetach", &detach_auto); git_config_date_string("gc.pruneexpire", &prune_expire); + git_config_date_string("gc.pruneworktreesexpire", &prune_worktrees_expire); git_config(git_default_config, NULL); } @@ -290,6 +293,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL); argv_array_pushl(&repack, "repack", "-d", "-l", NULL); argv_array_pushl(&prune, "prune", "--expire", NULL); + argv_array_pushl(&prune_worktrees, "prune", "--worktrees", "--expire", NULL); argv_array_pushl(&rerere, "rerere", "gc", NULL); gc_config(); @@ -359,6 +363,12 @@ int cmd_gc(int argc, const char **argv, const char *prefix) return error(FAILED_RUN, prune.argv[0]); } + if (prune_worktrees_expire) { + argv_array_push(&prune_worktrees, prune_worktrees_expire); + if (run_command_v_opt(prune_worktrees.argv, RUN_GIT_CMD)) + return error(FAILED_RUN, prune_worktrees.argv[0]); + } + if (run_command_v_opt(rerere.argv, RUN_GIT_CMD)) return error(FAILED_RUN, rerere.argv[0]); -- 2.1.0.rc0.78.gc0d8480 -- 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 26/34] gc: style change -- no SP before closing parenthesis
Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/gc.c b/builtin/gc.c index 005adbe..0e65eff 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -287,7 +287,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) argv_array_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL); argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL); argv_array_pushl(&repack, "repack", "-d", "-l", NULL); - argv_array_pushl(&prune, "prune", "--expire", NULL ); + argv_array_pushl(&prune, "prune", "--expire", NULL); argv_array_pushl(&rerere, "rerere", "gc", NULL); gc_config(); -- 2.1.0.rc0.78.gc0d8480 -- 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 30/34] git_path(): keep "info/sparse-checkout" per work-tree
Currently git_path("info/sparse-checkout") resolves to $GIT_COMMON_DIR/info/sparse-checkout in multiple worktree mode. It makes more sense for the sparse checkout patterns to be per worktree, so you can have multiple checkouts with different parts of the tree. With this, "git checkout --to " on a sparse checkout will create as a full checkout. Which is expected, it's how a new checkout is made. The user can reshape the worktree afterwards. Signed-off-by: Nguyễn Thái Ngọc Duy --- path.c| 3 ++- t/t0060-path-utils.sh | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/path.c b/path.c index cd8e2d6..35d498e 100644 --- a/path.c +++ b/path.c @@ -103,7 +103,8 @@ static void update_common_dir(struct strbuf *buf, int git_dir_len) char *base = buf->buf + git_dir_len; const char **p; - if (is_dir_file(base, "logs", "HEAD")) + if (is_dir_file(base, "logs", "HEAD") || + is_dir_file(base, "info", "sparse-checkout")) return; /* keep this in $GIT_DIR */ for (p = common_list; *p; p++) { const char *path = *p; diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index f5d6f80..93605f4 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -270,6 +270,7 @@ test_git_path GIT_COMMON_DIR=bar objects bar/objects test_git_path GIT_COMMON_DIR=bar objects/bar bar/objects/bar test_git_path GIT_COMMON_DIR=bar info/exclude bar/info/exclude test_git_path GIT_COMMON_DIR=bar info/grafts bar/info/grafts +test_git_path GIT_COMMON_DIR=bar info/sparse-checkout .git/info/sparse-checkout test_git_path GIT_COMMON_DIR=bar remotes/bar bar/remotes/bar test_git_path GIT_COMMON_DIR=bar branches/bar bar/branches/bar test_git_path GIT_COMMON_DIR=bar logs/refs/heads/master bar/logs/refs/heads/master -- 2.1.0.rc0.78.gc0d8480 -- 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 31/34] checkout: don't require a work tree when checking out into a new one
From: Dennis Kaarsemaker For normal use cases, it does not make sense for 'checkout' to work on a bare repository, without a worktree. But "checkout --to" is an exception because it _creates_ a new worktree. Allow this option to run on bare repositories. People who check out from a bare repository should remember that core.logallrefupdates is off by default and it should be turned back on. `--to` cannot do this automatically behind the user's back because some user may deliberately want no reflog. For people interested in repository setup/discovery code, is_bare_repository_cfg (aka "core.bare") is unchanged by this patch, which means 'true' by default for bare repos. Fortunately when we get the repo through a linked checkout, is_bare_repository_cfg is never used. So all is still good. [nd: commit message] Signed-off-by: Dennis Kaarsemaker Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/checkout.c | 3 +++ git.c | 2 +- t/t2025-checkout-to.sh | 15 +++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 5dfdbda..8c5276c 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1364,6 +1364,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) if (opts.new_worktree_mode) opts.new_worktree = NULL; + if (!opts.new_worktree) + setup_work_tree(); + if (conflict_style) { opts.merge = 1; /* implied */ git_xmerge_config("merge.conflictstyle", conflict_style, NULL); diff --git a/git.c b/git.c index 18fbf79..160896a 100644 --- a/git.c +++ b/git.c @@ -383,7 +383,7 @@ static struct cmd_struct commands[] = { { "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE }, { "check-mailmap", cmd_check_mailmap, RUN_SETUP }, { "check-ref-format", cmd_check_ref_format }, - { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE }, + { "checkout", cmd_checkout, RUN_SETUP }, { "checkout-index", cmd_checkout_index, RUN_SETUP | NEED_WORK_TREE}, { "cherry", cmd_cherry, RUN_SETUP }, diff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh index e2db078..4bd1df4 100755 --- a/t/t2025-checkout-to.sh +++ b/t/t2025-checkout-to.sh @@ -81,4 +81,19 @@ test_expect_success 'not die on re-checking out current branch' ' ) ' +test_expect_success 'checkout --to from a bare repo' ' + ( + git clone --bare . bare && + cd bare && + git checkout --to ../there2 -b bare-master master + ) +' + +test_expect_success 'checkout from a bare repo without --to' ' + ( + cd bare && + test_must_fail git checkout master + ) +' + test_done -- 2.1.0.rc0.78.gc0d8480 -- 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 22/34] checkout: support checking out into a new working directory
"git checkout --to" sets up a new working directory with a .git file pointing to $GIT_DIR/worktrees/. It then executes "git checkout" again on the new worktree with the same arguments except "--to" is taken out. The second checkout execution, which is not contaminated with any info from the current repository, will actually check out and everything that normal "git checkout" does. Helped-by: Marc Branchaud Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/git-checkout.txt | 46 Documentation/git.txt | 3 +- Documentation/gitrepository-layout.txt | 7 +++ builtin/checkout.c | 95 +- path.c | 2 +- t/t2025-checkout-to.sh (new +x)| 63 ++ 6 files changed, 212 insertions(+), 4 deletions(-) create mode 100755 t/t2025-checkout-to.sh diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 33ad2ad..c101575 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -225,6 +225,13 @@ This means that you can use `git checkout -p` to selectively discard edits from your current working tree. See the ``Interactive Mode'' section of linkgit:git-add[1] to learn how to operate the `--patch` mode. +--to=:: + Check out a branch in a separate working directory at + ``. A new working directory is linked to the current + repository, sharing everything except working directory + specific files such as HEAD, index... See "MULTIPLE WORKING + TREES" section for more information. + :: Branch to checkout; if it refers to a branch (i.e., a name that, when prepended with "refs/heads/", is a valid ref), then that @@ -388,6 +395,45 @@ $ git reflog -2 HEAD # or $ git log -g -2 HEAD +MULTIPLE WORKING TREES +-- + +A git repository can support multiple working trees, allowing you to check +out more than one branch at a time. With `git checkout --to` a new working +tree is associated with the repository. This new working tree is called a +"linked working tree" as opposed to the "main working tree" prepared by "git +init" or "git clone". A repository has one main working tree (if it's not a +bare repository) and zero or more linked working trees. + +Each linked working tree has a private sub-directory in the repository's +$GIT_DIR/worktrees directory. The private sub-directory's name is usually +the base name of the linked working tree's path, possibly appended with a +number to make it unique. For example, when `$GIT_DIR=/path/main/.git` the +command `git checkout --to /path/other/test-next next` creates the linked +working tree in `/path/other/test-next` and also creates a +`$GIT_DIR/worktrees/test-next` directory (or `$GIT_DIR/worktrees/test-next1` +if `test-next` is already taken). + +Within a linked working tree, $GIT_DIR is set to point to this private +directory (e.g. `/path/main/.git/worktrees/test-next` in the example) and +$GIT_COMMON_DIR is set to point back to the main working tree's $GIT_DIR +(e.g. `/path/main/.git`). These settings are made in a `.git` file located at +the top directory of the linked working tree. + +Path resolution via `git rev-parse --git-path` uses either +$GIT_DIR or $GIT_COMMON_DIR depending on the path. For example, in the +linked working tree `git rev-parse --git-path HEAD` returns +`/path/main/.git/worktrees/test-next/HEAD` (not +`/path/other/test-next/.git/HEAD` or `/path/main/.git/HEAD`) while `git +rev-parse --git-path refs/heads/master` uses +$GIT_COMMON_DIR and returns `/path/main/.git/refs/heads/master`, +since refs are shared across all working trees. + +See linkgit:gitrepository-layout[5] for more information. The rule of +thumb is do not make any assumption about whether a path belongs to +$GIT_DIR or $GIT_COMMON_DIR when you need to directly access something +inside $GIT_DIR. Use `git rev-parse --git-path` to get the final path. + EXAMPLES diff --git a/Documentation/git.txt b/Documentation/git.txt index 96354af..0c2dcfa 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -813,7 +813,8 @@ Git so take care if using Cogito etc. If this variable is set to a path, non-worktree files that are normally in $GIT_DIR will be taken from this path instead. Worktree-specific files such as HEAD or index are - taken from $GIT_DIR. See linkgit:gitrepository-layout[5] for + taken from $GIT_DIR. See linkgit:gitrepository-layout[5] and + the section 'MULTIPLE CHECKOUT MODE' in linkgit:checkout[1] details. This variable has lower precedence than other path variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY... diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt index 2dc5667..8228450 100644 --- a/Documentation/gitrepository-layout.txt +++ b/Documentation/gitrepository-layout.txt
[PATCH 32/34] t2025: add a test to make sure grafts is working from a linked checkout
Signed-off-by: Nguyễn Thái Ngọc Duy --- t/t2025-checkout-to.sh | 18 ++ 1 file changed, 18 insertions(+) diff --git a/t/t2025-checkout-to.sh b/t/t2025-checkout-to.sh index 4bd1df4..eddd325 100755 --- a/t/t2025-checkout-to.sh +++ b/t/t2025-checkout-to.sh @@ -96,4 +96,22 @@ test_expect_success 'checkout from a bare repo without --to' ' ) ' +test_expect_success 'checkout with grafts' ' + test_when_finished rm .git/info/grafts && + test_commit abc && + SHA1=`git rev-parse HEAD` && + test_commit def && + test_commit xyz && + echo "`git rev-parse HEAD` $SHA1" >.git/info/grafts && + cat >expected <<-\EOF && + xyz + abc + EOF + git log --format=%s -2 >actual && + test_cmp expected actual && + git checkout --detach --to grafted master && + git --git-dir=grafted/.git log --format=%s -2 >actual && + test_cmp expected actual +' + test_done -- 2.1.0.rc0.78.gc0d8480 -- 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 24/34] checkout: reject if the branch is already checked out elsewhere
One branch obviously can't be checked out at two places (but detached heads are ok). Give the user a choice in this case: --detach, -b new-branch, switch branch in the other checkout first or simply 'cd' and continue to work there. Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/checkout.c | 86 -- t/t2025-checkout-to.sh | 25 --- 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 645135a..01a28b4 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -430,6 +430,11 @@ struct branch_info { const char *name; /* The short name used */ const char *path; /* The full name of a real branch */ struct commit *commit; /* The named commit */ + /* +* if not null the branch is detached because it's already +* checked out in this checkout +*/ + char *checkout; }; static void setup_branch_path(struct branch_info *branch) @@ -958,12 +963,78 @@ static const char *unique_tracking_name(const char *name, unsigned char *sha1) return NULL; } +static void check_linked_checkout(struct branch_info *new, const char *id) +{ + struct strbuf sb = STRBUF_INIT; + struct strbuf path = STRBUF_INIT; + struct strbuf gitdir = STRBUF_INIT; + const char *start, *end; + + if (id) + strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id); + else + strbuf_addf(&path, "%s/HEAD", get_git_common_dir()); + + if (strbuf_read_file(&sb, path.buf, 0) < 0 || + !skip_prefix(sb.buf, "ref:", &start)) + goto done; + while (isspace(*start)) + start++; + end = start; + while (*end && !isspace(*end)) + end++; + if (strncmp(start, new->path, end - start) || new->path[end - start] != '\0') + goto done; + if (id) { + strbuf_reset(&path); + strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id); + if (strbuf_read_file(&gitdir, path.buf, 0) <= 0) + goto done; + strbuf_rtrim(&gitdir); + } else + strbuf_addstr(&gitdir, get_git_common_dir()); + die(_("'%s' is already checked out at '%s'"), new->name, gitdir.buf); +done: + strbuf_release(&path); + strbuf_release(&sb); + strbuf_release(&gitdir); +} + +static void check_linked_checkouts(struct branch_info *new) +{ + struct strbuf path = STRBUF_INIT; + DIR *dir; + struct dirent *d; + + strbuf_addf(&path, "%s/worktrees", get_git_common_dir()); + if ((dir = opendir(path.buf)) == NULL) { + strbuf_release(&path); + return; + } + + /* +* $GIT_COMMON_DIR/HEAD is practically outside +* $GIT_DIR so resolve_ref_unsafe() won't work (it +* uses git_path). Parse the ref ourselves. +*/ + check_linked_checkout(new, NULL); + + while ((d = readdir(dir)) != NULL) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + check_linked_checkout(new, d->d_name); + } + strbuf_release(&path); + closedir(dir); +} + static int parse_branchname_arg(int argc, const char **argv, int dwim_new_local_branch_ok, struct branch_info *new, struct tree **source_tree, unsigned char rev[20], - const char **new_branch) + const char **new_branch, + int force_detach) { int argcount = 0; unsigned char branch_rev[20]; @@ -1085,6 +1156,16 @@ static int parse_branchname_arg(int argc, const char **argv, else new->path = NULL; /* not an existing branch */ + if (new->path && !force_detach && !*new_branch) { + unsigned char sha1[20]; + int flag; + char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag); + if (head_ref && + (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path))) + check_linked_checkouts(new); + free(head_ref); + } + new->commit = lookup_commit_reference_gently(rev, 1); if (!new->commit) { /* not a commit */ @@ -1289,7 +1370,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) !opts.new_branch; int n = parse_branchname_arg(argc, argv, dwim_ok, &new, &opts.source_tree, -rev, &opts.new_branch); +rev, &opts.new_branch, +
[PATCH 27/34] gc: factor out gc.pruneexpire parsing code
Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/gc.c | 22 -- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/builtin/gc.c b/builtin/gc.c index 0e65eff..0728650 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -55,6 +55,17 @@ static void remove_pidfile_on_signal(int signo) raise(signo); } +static void git_config_date_string(const char *key, const char **output) +{ + if (git_config_get_string_const(key, output)) + return; + if (strcmp(*output, "now")) { + unsigned long now = approxidate("now"); + if (approxidate(*output) >= now) + git_die_config(key, _("Invalid %s: '%s'"), key, *output); + } +} + static void gc_config(void) { const char *value; @@ -71,16 +82,7 @@ static void gc_config(void) git_config_get_int("gc.auto", &gc_auto_threshold); git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit); git_config_get_bool("gc.autodetach", &detach_auto); - - if (!git_config_get_string_const("gc.pruneexpire", &prune_expire)) { - if (strcmp(prune_expire, "now")) { - unsigned long now = approxidate("now"); - if (approxidate(prune_expire) >= now) { - git_die_config("gc.pruneexpire", _("Invalid gc.pruneexpire: '%s'"), - prune_expire); - } - } - } + git_config_date_string("gc.pruneexpire", &prune_expire); git_config(git_default_config, NULL); } -- 2.1.0.rc0.78.gc0d8480 -- 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 29/34] count-objects: report unused files in $GIT_DIR/worktrees/...
In linked checkouts, borrowed parts like config is taken from $GIT_COMMON_DIR. $GIT_DIR/config is never used. Report them as garbage. Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/count-objects.c | 4 +++- cache.h | 1 + path.c | 29 +++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/builtin/count-objects.c b/builtin/count-objects.c index e47ef0b..ad0c799 100644 --- a/builtin/count-objects.c +++ b/builtin/count-objects.c @@ -70,8 +70,10 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix) /* we do not take arguments other than flags for now */ if (argc) usage_with_options(count_objects_usage, opts); - if (verbose) + if (verbose) { report_garbage = real_report_garbage; + report_linked_checkout_garbage(); + } for_each_loose_file_in_objdir(get_object_directory(), count_loose, count_cruft, NULL, NULL); diff --git a/cache.h b/cache.h index f8e75a1..3f60a11 100644 --- a/cache.h +++ b/cache.h @@ -695,6 +695,7 @@ extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1 extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); extern const char *git_path_submodule(const char *path, const char *fmt, ...) __attribute__((format (printf, 2, 3))); +extern void report_linked_checkout_garbage(void); /* * Return the name of the file in the local object database that would diff --git a/path.c b/path.c index 72eca6d..cd8e2d6 100644 --- a/path.c +++ b/path.c @@ -4,6 +4,7 @@ #include "cache.h" #include "strbuf.h" #include "string-list.h" +#include "dir.h" static int get_st_mode_bits(const char *path, int *mode) { @@ -91,9 +92,9 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir) } static const char *common_list[] = { - "/branches", "/hooks", "/info", "/logs", "/lost-found", "/modules", + "/branches", "/hooks", "/info", "!/logs", "/lost-found", "/modules", "/objects", "/refs", "/remotes", "/worktrees", "/rr-cache", "/svn", - "config", "gc.pid", "packed-refs", "shallow", + "config", "!gc.pid", "packed-refs", "shallow", NULL }; @@ -107,6 +108,8 @@ static void update_common_dir(struct strbuf *buf, int git_dir_len) for (p = common_list; *p; p++) { const char *path = *p; int is_dir = 0; + if (*path == '!') + path++; if (*path == '/') { path++; is_dir = 1; @@ -122,6 +125,28 @@ static void update_common_dir(struct strbuf *buf, int git_dir_len) } } +void report_linked_checkout_garbage(void) +{ + struct strbuf sb = STRBUF_INIT; + const char **p; + int len; + + if (!git_common_dir_env) + return; + strbuf_addf(&sb, "%s/", get_git_dir()); + len = sb.len; + for (p = common_list; *p; p++) { + const char *path = *p; + if (*path == '!') + continue; + strbuf_setlen(&sb, len); + strbuf_addstr(&sb, path); + if (file_exists(sb.buf)) + report_garbage("unused in linked checkout", sb.buf); + } + strbuf_release(&sb); +} + static void adjust_git_path(struct strbuf *buf, int git_dir_len) { const char *base = buf->buf + git_dir_len; -- 2.1.0.rc0.78.gc0d8480 -- 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 16/34] setup.c: detect $GIT_COMMON_DIR in is_git_directory()
If the file "$GIT_DIR/commondir" exists, it contains the value of $GIT_COMMON_DIR. Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/gitrepository-layout.txt | 7 ++ setup.c| 43 +- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt index 2b5966a..2dc5667 100644 --- a/Documentation/gitrepository-layout.txt +++ b/Documentation/gitrepository-layout.txt @@ -240,6 +240,13 @@ shallow:: file is ignored if $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/shallow" will be used instead. +commondir:: + If this file exists, $GIT_COMMON_DIR (see linkgit:git[1]) will + be set to the path specified in this file if it is not + explicitly set. If the specified path is relative, it is + relative to $GIT_DIR. The repository with commondir is + incomplete without the repository pointed by "commondir". + modules:: Contains the git-repositories of the submodules. This directory is ignored if $GIT_COMMON_DIR is set and diff --git a/setup.c b/setup.c index afd6742..6c52f75 100644 --- a/setup.c +++ b/setup.c @@ -224,6 +224,33 @@ void verify_non_filename(const char *prefix, const char *arg) "'git [...] -- [...]'", arg); } +static void get_common_dir(struct strbuf *sb, const char *gitdir) +{ + struct strbuf data = STRBUF_INIT; + struct strbuf path = STRBUF_INIT; + const char *git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT); + if (git_common_dir) { + strbuf_addstr(sb, git_common_dir); + return; + } + strbuf_addf(&path, "%s/commondir", gitdir); + if (file_exists(path.buf)) { + if (strbuf_read_file(&data, path.buf, 0) <= 0) + die_errno(_("failed to read %s"), path.buf); + while (data.len && (data.buf[data.len - 1] == '\n' || + data.buf[data.len - 1] == '\r')) + data.len--; + data.buf[data.len] = '\0'; + strbuf_reset(&path); + if (!is_absolute_path(data.buf)) + strbuf_addf(&path, "%s/", gitdir); + strbuf_addbuf(&path, &data); + strbuf_addstr(sb, real_path(path.buf)); + } else + strbuf_addstr(sb, gitdir); + strbuf_release(&data); + strbuf_release(&path); +} /* * Test if it looks like we're at a git directory. @@ -242,13 +269,22 @@ int is_git_directory(const char *suspect) int ret = 0; size_t len; - strbuf_addstr(&path, suspect); + /* Check worktree-related signatures */ + strbuf_addf(&path, "%s/HEAD", suspect); + if (validate_headref(path.buf)) + goto done; + + strbuf_reset(&path); + get_common_dir(&path, suspect); len = path.len; + + /* Check non-worktree-related signatures */ if (getenv(DB_ENVIRONMENT)) { if (access(getenv(DB_ENVIRONMENT), X_OK)) goto done; } else { + strbuf_setlen(&path, len); strbuf_addstr(&path, "/objects"); if (access(path.buf, X_OK)) goto done; @@ -259,11 +295,6 @@ int is_git_directory(const char *suspect) if (access(path.buf, X_OK)) goto done; - strbuf_setlen(&path, len); - strbuf_addstr(&path, "/HEAD"); - if (validate_headref(path.buf)) - goto done; - ret = 1; done: strbuf_release(&path); -- 2.1.0.rc0.78.gc0d8480 -- 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 15/34] setup.c: convert is_git_directory() to use strbuf
Signed-off-by: Nguyễn Thái Ngọc Duy --- setup.c | 37 + 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/setup.c b/setup.c index 979b13f..afd6742 100644 --- a/setup.c +++ b/setup.c @@ -238,31 +238,36 @@ void verify_non_filename(const char *prefix, const char *arg) */ int is_git_directory(const char *suspect) { - char path[PATH_MAX]; - size_t len = strlen(suspect); + struct strbuf path = STRBUF_INIT; + int ret = 0; + size_t len; - if (PATH_MAX <= len + strlen("/objects")) - die("Too long path: %.*s", 60, suspect); - strcpy(path, suspect); + strbuf_addstr(&path, suspect); + len = path.len; if (getenv(DB_ENVIRONMENT)) { if (access(getenv(DB_ENVIRONMENT), X_OK)) - return 0; + goto done; } else { - strcpy(path + len, "/objects"); - if (access(path, X_OK)) - return 0; + strbuf_addstr(&path, "/objects"); + if (access(path.buf, X_OK)) + goto done; } - strcpy(path + len, "/refs"); - if (access(path, X_OK)) - return 0; + strbuf_setlen(&path, len); + strbuf_addstr(&path, "/refs"); + if (access(path.buf, X_OK)) + goto done; - strcpy(path + len, "/HEAD"); - if (validate_headref(path)) - return 0; + strbuf_setlen(&path, len); + strbuf_addstr(&path, "/HEAD"); + if (validate_headref(path.buf)) + goto done; - return 1; + ret = 1; +done: + strbuf_release(&path); + return ret; } int is_inside_git_dir(void) -- 2.1.0.rc0.78.gc0d8480 -- 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 20/34] wrapper.c: wrapper to open a file, fprintf then close
Signed-off-by: Nguyễn Thái Ngọc Duy --- cache.h | 2 ++ wrapper.c | 31 +++ 2 files changed, 33 insertions(+) diff --git a/cache.h b/cache.h index 9dc6ae0..f8e75a1 100644 --- a/cache.h +++ b/cache.h @@ -1499,6 +1499,8 @@ static inline ssize_t write_str_in_full(int fd, const char *str) { return write_in_full(fd, str, strlen(str)); } +__attribute__((format (printf,3,4))) +extern int write_file(const char *path, int fatal, const char *fmt, ...); /* pager.c */ extern void setup_pager(void); diff --git a/wrapper.c b/wrapper.c index 007ec0d..a2dff8e 100644 --- a/wrapper.c +++ b/wrapper.c @@ -550,3 +550,34 @@ char *xgetcwd(void) die_errno(_("unable to get current working directory")); return strbuf_detach(&sb, NULL); } + +int write_file(const char *path, int fatal, const char *fmt, ...) +{ + struct strbuf sb = STRBUF_INIT; + va_list params; + int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); + if (fd < 0) { + if (fatal) + die_errno(_("could not open %s for writing"), path); + return -1; + } + va_start(params, fmt); + strbuf_vaddf(&sb, fmt, params); + va_end(params); + if (write_in_full(fd, sb.buf, sb.len) != sb.len) { + int err = errno; + close(fd); + strbuf_release(&sb); + errno = err; + if (fatal) + die_errno(_("could not write to %s"), path); + return -1; + } + strbuf_release(&sb); + if (close(fd)) { + if (fatal) + die_errno(_("could not close %s"), path); + return -1; + } + return 0; +} -- 2.1.0.rc0.78.gc0d8480 -- 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 21/34] use new wrapper write_file() for simple file writing
This fixes common problems in these code about error handling, forgetting to close the file handle after fprintf() fails, or not printing out the error string.. Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/branch.c | 4 +--- builtin/init-db.c | 7 +-- daemon.c | 11 +-- submodule.c | 9 ++--- transport.c | 8 +++- 5 files changed, 8 insertions(+), 31 deletions(-) diff --git a/builtin/branch.c b/builtin/branch.c index 3b79c50..1a475d8 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -764,7 +764,6 @@ static const char edit_description[] = "BRANCH_DESCRIPTION"; static int edit_branch_description(const char *branch_name) { - FILE *fp; int status; struct strbuf buf = STRBUF_INIT; struct strbuf name = STRBUF_INIT; @@ -777,8 +776,7 @@ static int edit_branch_description(const char *branch_name) " %s\n" "Lines starting with '%c' will be stripped.\n", branch_name, comment_line_char); - fp = fopen(git_path(edit_description), "w"); - if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) { + if (write_file(git_path(edit_description), 0, "%s", buf.buf)) { strbuf_release(&buf); return error(_("could not write branch description template: %s"), strerror(errno)); diff --git a/builtin/init-db.c b/builtin/init-db.c index 587a505..6b7fa5f 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -342,7 +342,6 @@ int set_git_dir_init(const char *git_dir, const char *real_git_dir, static void separate_git_dir(const char *git_dir) { struct stat st; - FILE *fp; if (!stat(git_link, &st)) { const char *src; @@ -358,11 +357,7 @@ static void separate_git_dir(const char *git_dir) die_errno(_("unable to move %s to %s"), src, git_dir); } - fp = fopen(git_link, "w"); - if (!fp) - die(_("Could not create git link %s"), git_link); - fprintf(fp, "gitdir: %s\n", git_dir); - fclose(fp); + write_file(git_link, 1, "gitdir: %s\n", git_dir); } int init_db(const char *template_dir, unsigned int flags) diff --git a/daemon.c b/daemon.c index 54a03bd..60bbab6 100644 --- a/daemon.c +++ b/daemon.c @@ -1070,15 +1070,6 @@ static struct credentials *prepare_credentials(const char *user_name, } #endif -static void store_pid(const char *path) -{ - FILE *f = fopen(path, "w"); - if (!f) - die_errno("cannot open pid file '%s'", path); - if (fprintf(f, "%"PRIuMAX"\n", (uintmax_t) getpid()) < 0 || fclose(f) != 0) - die_errno("failed to write pid file '%s'", path); -} - static int serve(struct string_list *listen_addr, int listen_port, struct credentials *cred) { @@ -1289,7 +1280,7 @@ int main(int argc, char **argv) sanitize_stdfds(); if (pid_file) - store_pid(pid_file); + write_file(pid_file, 1, "%"PRIuMAX"\n", (uintmax_t) getpid()); /* prepare argv for serving-processes */ cld_argv = xmalloc(sizeof (char *) * (argc + 2)); diff --git a/submodule.c b/submodule.c index 0690dc5..34094f5 100644 --- a/submodule.c +++ b/submodule.c @@ -1102,16 +1102,11 @@ void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir) struct strbuf file_name = STRBUF_INIT; struct strbuf rel_path = STRBUF_INIT; const char *real_work_tree = xstrdup(real_path(work_tree)); - FILE *fp; /* Update gitfile */ strbuf_addf(&file_name, "%s/.git", work_tree); - fp = fopen(file_name.buf, "w"); - if (!fp) - die(_("Could not create git link %s"), file_name.buf); - fprintf(fp, "gitdir: %s\n", relative_path(git_dir, real_work_tree, - &rel_path)); - fclose(fp); + write_file(file_name.buf, 1, "gitdir: %s\n", + relative_path(git_dir, real_work_tree, &rel_path)); /* Update core.worktree setting */ strbuf_reset(&file_name); diff --git a/transport.c b/transport.c index 70d38e4..051b7ac 100644 --- a/transport.c +++ b/transport.c @@ -283,7 +283,6 @@ static int write_one_ref(const char *name, const unsigned char *sha1, { struct strbuf *buf = data; int len = buf->len; - FILE *f; /* when called via for_each_ref(), flags is non-zero */ if (flags && !starts_with(name, "refs/heads/") && @@ -292,10 +291,9 @@ static int write_one_ref(const char *name, const unsigned char *sha1, strbuf_addstr(buf, name); if (safe_create_leading_directories(buf->buf) || - !(f = fopen(buf->buf, "w")) || - fprintf(f, "%s\n", sha1_to_hex(sha1)) < 0 || - fclose(f)) - return error("problems writing temporary file %s", buf->b
[PATCH 12/34] git-sh-setup.sh: use rev-parse --git-path to get $GIT_DIR/objects
If $GIT_COMMON_DIR is set, $GIT_OBJECT_DIRECTORY should be $GIT_COMMON_DIR/objects, not $GIT_DIR/objects. Just let rev-parse --git-path handle it. Signed-off-by: Nguyễn Thái Ngọc Duy --- git-sh-setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-sh-setup.sh b/git-sh-setup.sh index d968760..94a5c6d 100644 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -344,7 +344,7 @@ git_dir_init () { echo >&2 "Unable to determine absolute path of git directory" exit 1 } - : ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"} + : ${GIT_OBJECT_DIRECTORY="$(git rev-parse --git-path objects)"} } if test -z "$NONGIT_OK" -- 2.1.0.rc0.78.gc0d8480 -- 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 18/34] setup.c: detect $GIT_COMMON_DIR check_repository_format_gently()
Signed-off-by: Nguyễn Thái Ngọc Duy --- setup.c | 6 -- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/setup.c b/setup.c index 00a23e6..1d4f1aa 100644 --- a/setup.c +++ b/setup.c @@ -346,6 +346,10 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok) const char *repo_config; int ret = 0; + get_common_dir(&sb, gitdir); + strbuf_addstr(&sb, "/config"); + repo_config = sb.buf; + /* * git_config() can't be used here because it calls git_pathdup() * to get $GIT_CONFIG/config. That call will make setup_git_env() @@ -355,8 +359,6 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok) * Use a gentler version of git_config() to check if this repo * is a good one. */ - strbuf_addf(&sb, "%s/config", gitdir); - repo_config = sb.buf; git_config_early(check_repository_format_version, NULL, repo_config); if (GIT_REPO_VERSION < repository_format_version) { if (!nongit_ok) -- 2.1.0.rc0.78.gc0d8480 -- 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 13/34] *.sh: avoid hardcoding $GIT_DIR/hooks/...
If $GIT_COMMON_DIR is set, it should be $GIT_COMMON_DIR/hooks/, not $GIT_DIR/hooks/. Just let rev-parse --git-path handle it. Signed-off-by: Nguyễn Thái Ngọc Duy --- git-am.sh | 22 +++--- git-rebase--interactive.sh | 6 +++--- git-rebase--merge.sh | 6 ++ git-rebase.sh | 4 ++-- templates/hooks--applypatch-msg.sample | 4 ++-- templates/hooks--pre-applypatch.sample | 4 ++-- 6 files changed, 22 insertions(+), 24 deletions(-) diff --git a/git-am.sh b/git-am.sh index ee61a77..66803d1 100755 --- a/git-am.sh +++ b/git-am.sh @@ -810,10 +810,10 @@ To restore the original branch and stop patching run \"\$cmdline --abort\"." continue fi - if test -x "$GIT_DIR"/hooks/applypatch-msg + hook="$(git rev-parse --git-path hooks/applypatch-msg)" + if test -x "$hook" then - "$GIT_DIR"/hooks/applypatch-msg "$dotest/final-commit" || - stop_here $this + "$hook" "$dotest/final-commit" || stop_here $this fi if test -f "$dotest/final-commit" @@ -887,9 +887,10 @@ did you forget to use 'git add'?" stop_here_user_resolve $this fi - if test -x "$GIT_DIR"/hooks/pre-applypatch + hook="$(git rev-parse --git-path hooks/pre-applypatch)" + if test -x "$hook" then - "$GIT_DIR"/hooks/pre-applypatch || stop_here $this + "$hook" || stop_here $this fi tree=$(git write-tree) && @@ -916,18 +917,17 @@ did you forget to use 'git add'?" echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten" fi - if test -x "$GIT_DIR"/hooks/post-applypatch - then - "$GIT_DIR"/hooks/post-applypatch - fi + hook="$(git rev-parse --git-path hooks/post-applypatch)" + test -x "$hook" && "$hook" go_next done if test -s "$dotest"/rewritten; then git notes copy --for-rewrite=rebase < "$dotest"/rewritten -if test -x "$GIT_DIR"/hooks/post-rewrite; then - "$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten +hook="$(git rev-parse --git-path hooks/post-rewrite)" +if test -x "$hook"; then + "$hook" rebase < "$dotest"/rewritten fi fi diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index b64dd28..b32f797 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -642,9 +642,9 @@ do_next () { git notes copy --for-rewrite=rebase < "$rewritten_list" || true # we don't care if this copying failed } && - if test -x "$GIT_DIR"/hooks/post-rewrite && - test -s "$rewritten_list"; then - "$GIT_DIR"/hooks/post-rewrite rebase < "$rewritten_list" + hook="$(git rev-parse --git-path hooks/post-rewrite)" + if test -x "$hook" && test -s "$rewritten_list"; then + "$hook" rebase < "$rewritten_list" true # we don't care if this hook failed fi && warn "Successfully rebased and updated $head_name." diff --git a/git-rebase--merge.sh b/git-rebase--merge.sh index d3fb67d..2cc2a6d 100644 --- a/git-rebase--merge.sh +++ b/git-rebase--merge.sh @@ -94,10 +94,8 @@ finish_rb_merge () { if test -s "$state_dir"/rewritten then git notes copy --for-rewrite=rebase <"$state_dir"/rewritten - if test -x "$GIT_DIR"/hooks/post-rewrite - then - "$GIT_DIR"/hooks/post-rewrite rebase <"$state_dir"/rewritten - fi + hook="$(git rev-parse --git-path hooks/post-rewrite)" + test -x "$hook" && "$hook" rebase <"$state_dir"/rewritten fi say All done. } diff --git a/git-rebase.sh b/git-rebase.sh index 55da9db..fb935a0 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -202,9 +202,9 @@ run_specific_rebase () { run_pre_rebase_hook () { if test -z "$ok_to_skip_pre_rebase" && - test -x "$GIT_DIR/hooks/pre-rebase" + test -x "$(git rev-parse --git-path hooks/pre-rebase)" then - "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || + "$(git rev-parse --git-path hooks/pre-rebase)" ${1+"$@"} || die "$(gettext "The pre-rebase hook refused to rebase.")" fi } diff --git a/templates/hooks--applypatch-msg.sample b/templates/hooks--applypatch-msg.sample index 8b2a2fe..a5d7b84 100755 --- a/templates/hooks--applypatch-msg.sample +++ b/templates/hooks--applypatch-msg.sample @@ -10,6 +10,6 @@ # To enable this hook, rename this file to "applypatch-msg". . git-sh-setup -test -x "$GIT_DIR/hooks/commit-msg" && - exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} +commitmsg="$(git rev-parse --git-path hooks/commit-msg)" +test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} : diff --git a/templates/hooks--pre-applypa
[PATCH 14/34] git-stash: avoid hardcoding $GIT_DIR/logs/....
Signed-off-by: Nguyễn Thái Ngọc Duy --- git-stash.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-stash.sh b/git-stash.sh index b6d4b06..6846b18 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -184,7 +184,7 @@ store_stash () { fi # Make sure the reflog for stash is kept. - : >>"$GIT_DIR/logs/$ref_stash" + : >>"$(git rev-parse --git-path logs/$ref_stash)" git update-ref -m "$stash_msg" $ref_stash $w_commit ret=$? test $ret != 0 && test -z $quiet && @@ -259,7 +259,7 @@ save_stash () { say "$(gettext "No local changes to save")" exit 0 fi - test -f "$GIT_DIR/logs/$ref_stash" || + test -f "$(git rev-parse --git-path logs/$ref_stash)" || clear_stash || die "$(gettext "Cannot initialize stash")" create_stash "$stash_msg" $untracked -- 2.1.0.rc0.78.gc0d8480 -- 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 19/34] setup.c: support multi-checkout repo setup
The repo setup procedure is updated to detect $GIT_DIR/commondir and set $GIT_COMMON_DIR properly. The core.worktree is ignored when $GIT_COMMON_DIR is set. This is because the config file is shared in multi-checkout setup, but checkout directories _are_ different. Making core.worktree effective in all checkouts mean it's back to a single checkout. Helped-by: Johannes Sixt Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/config.txt| 2 ++ Documentation/git-rev-parse.txt | 3 ++ builtin/rev-parse.c | 4 +++ cache.h | 1 + environment.c | 8 ++--- setup.c | 33 +- t/t1501-worktree.sh | 76 + t/t1510-repo-setup.sh | 1 + trace.c | 1 + 9 files changed, 115 insertions(+), 14 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 9220725..e1623ec 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -393,6 +393,8 @@ false), while all other repositories are assumed to be bare (bare core.worktree:: Set the path to the root of the working tree. + If GIT_COMMON_DIR environment variable is set, core.worktree + is ignored and not used for determining the root of working tree. This can be overridden by the GIT_WORK_TREE environment variable and the '--work-tree' command-line option. The value can be an absolute path or relative to the path to diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 2103bbf..97fc703 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -216,6 +216,9 @@ If `$GIT_DIR` is not defined and the current directory is not detected to lie in a Git repository or work tree print a message to stderr and exit with nonzero status. +--git-common-dir:: + Show `$GIT_COMMON_DIR` if defined, else `$GIT_DIR`. + --is-inside-git-dir:: When the current working directory is below the repository directory print "true", otherwise "false". diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 4cd44aa..5799f35 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -762,6 +762,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) free(cwd); continue; } + if (!strcmp(arg, "--git-common-dir")) { + puts(get_git_common_dir()); + continue; + } if (!strcmp(arg, "--resolve-git-dir")) { const char *gitdir = argv[++i]; if (!gitdir) diff --git a/cache.h b/cache.h index bce1a7d..9dc6ae0 100644 --- a/cache.h +++ b/cache.h @@ -437,6 +437,7 @@ extern char *get_object_directory(void); extern char *get_index_file(void); extern char *get_graft_file(void); extern int set_git_dir(const char *path); +extern int get_common_dir(struct strbuf *sb, const char *gitdir); extern const char *get_git_namespace(void); extern const char *strip_namespace(const char *namespaced_ref); extern const char *get_git_work_tree(void); diff --git a/environment.c b/environment.c index 050f160..8351007 100644 --- a/environment.c +++ b/environment.c @@ -142,6 +142,7 @@ static char *git_path_from_env(const char *envvar, const char *git_dir, static void setup_git_env(void) { + struct strbuf sb = STRBUF_INIT; const char *gitfile; const char *shallow_file; @@ -150,12 +151,9 @@ static void setup_git_env(void) git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; gitfile = read_gitfile(git_dir); git_dir = xstrdup(gitfile ? gitfile : git_dir); - git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT); - if (git_common_dir) { + if (get_common_dir(&sb, git_dir)) git_common_dir_env = 1; - git_common_dir = xstrdup(git_common_dir); - } else - git_common_dir = git_dir; + git_common_dir = strbuf_detach(&sb, NULL); git_object_dir = git_path_from_env(DB_ENVIRONMENT, git_common_dir, "objects", &git_db_env); git_index_file = git_path_from_env(INDEX_ENVIRONMENT, git_dir, diff --git a/setup.c b/setup.c index 1d4f1aa..b99bdd3 100644 --- a/setup.c +++ b/setup.c @@ -224,14 +224,15 @@ void verify_non_filename(const char *prefix, const char *arg) "'git [...] -- [...]'", arg); } -static void get_common_dir(struct strbuf *sb, const char *gitdir) +int get_common_dir(struct strbuf *sb, const char *gitdir) { struct strbuf data = STRBUF_INIT; struct strbuf path = STRBUF_INIT; const char *git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT); + int ret = 0; if (git_common
[PATCH 17/34] setup.c: convert check_repository_format_gently to use strbuf
Signed-off-by: Nguyễn Thái Ngọc Duy --- setup.c | 12 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/setup.c b/setup.c index 6c52f75..00a23e6 100644 --- a/setup.c +++ b/setup.c @@ -342,7 +342,9 @@ void setup_work_tree(void) static int check_repository_format_gently(const char *gitdir, int *nongit_ok) { - char repo_config[PATH_MAX+1]; + struct strbuf sb = STRBUF_INIT; + const char *repo_config; + int ret = 0; /* * git_config() can't be used here because it calls git_pathdup() @@ -353,7 +355,8 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok) * Use a gentler version of git_config() to check if this repo * is a good one. */ - snprintf(repo_config, PATH_MAX, "%s/config", gitdir); + strbuf_addf(&sb, "%s/config", gitdir); + repo_config = sb.buf; git_config_early(check_repository_format_version, NULL, repo_config); if (GIT_REPO_VERSION < repository_format_version) { if (!nongit_ok) @@ -363,9 +366,10 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok) GIT_REPO_VERSION, repository_format_version); warning("Please upgrade Git"); *nongit_ok = -1; - return -1; + ret = -1; } - return 0; + strbuf_release(&sb); + return ret; } /* -- 2.1.0.rc0.78.gc0d8480 -- 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 10/34] commit: use SEQ_DIR instead of hardcoding "sequencer"
Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/commit.c b/builtin/commit.c index e108c53..3f02686 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -170,7 +170,7 @@ static void determine_whence(struct wt_status *s) whence = FROM_MERGE; else if (file_exists(git_path("CHERRY_PICK_HEAD"))) { whence = FROM_CHERRY_PICK; - if (file_exists(git_path("sequencer"))) + if (file_exists(git_path(SEQ_DIR))) sequencer_in_use = 1; } else -- 2.1.0.rc0.78.gc0d8480 -- 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 11/34] $GIT_COMMON_DIR: a new environment variable
This variable is intended to support multiple working directories attached to a repository. Such a repository may have a main working directory, created by either "git init" or "git clone" and one or more linked working directories. These working directories and the main repository share the same repository directory. In linked working directories, $GIT_COMMON_DIR must be defined to point to the real repository directory and $GIT_DIR points to an unused subdirectory inside $GIT_COMMON_DIR. File locations inside the repository are reorganized from the linked worktree view point: - worktree-specific such as HEAD, logs/HEAD, index, other top-level refs and unrecognized files are from $GIT_DIR. - the rest like objects, refs, info, hooks, packed-refs, shallow... are from $GIT_COMMON_DIR (except info/sparse-checkout, but that's a separate patch) Scripts are supposed to retrieve paths in $GIT_DIR with "git rev-parse --git-path", which will take care of "$GIT_DIR vs $GIT_COMMON_DIR" business. The redirection is done by git_path(), git_pathdup() and strbuf_git_path(). The selected list of paths goes to $GIT_COMMON_DIR, not the other way around in case a developer adds a new worktree-specific file and it's accidentally promoted to be shared across repositories (this includes unknown files added by third party commands) The list of known files that belong to $GIT_DIR are: ADD_EDIT.patch BISECT_ANCESTORS_OK BISECT_EXPECTED_REV BISECT_LOG BISECT_NAMES CHERRY_PICK_HEAD COMMIT_MSG FETCH_HEAD HEAD MERGE_HEAD MERGE_MODE MERGE_RR NOTES_EDITMSG NOTES_MERGE_WORKTREE ORIG_HEAD REVERT_HEAD SQUASH_MSG TAG_EDITMSG fast_import_crash_* logs/HEAD next-index-* rebase-apply rebase-merge rsync-refs-* sequencer/* shallow_* Path mapping is NOT done for git_path_submodule(). Multi-checkouts are not supported as submodules. Helped-by: Jens Lehmann Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/git.txt | 8 ++ Documentation/gitrepository-layout.txt | 45 +- cache.h| 4 ++- environment.c | 29 -- path.c | 34 + t/t0060-path-utils.sh | 16 6 files changed, 117 insertions(+), 19 deletions(-) diff --git a/Documentation/git.txt b/Documentation/git.txt index afb48d3..96354af 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -809,6 +809,14 @@ Git so take care if using Cogito etc. an explicit repository directory set via 'GIT_DIR' or on the command line. +'GIT_COMMON_DIR':: + If this variable is set to a path, non-worktree files that are + normally in $GIT_DIR will be taken from this path + instead. Worktree-specific files such as HEAD or index are + taken from $GIT_DIR. See linkgit:gitrepository-layout[5] for + details. This variable has lower precedence than other path + variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY... + Git Commits ~~~ 'GIT_AUTHOR_NAME':: diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt index 79653f3..2b5966a 100644 --- a/Documentation/gitrepository-layout.txt +++ b/Documentation/gitrepository-layout.txt @@ -46,6 +46,9 @@ of incomplete object store is not suitable to be published for use with dumb transports but otherwise is OK as long as `objects/info/alternates` points at the object stores it borrows from. ++ +This directory is ignored if $GIT_COMMON_DIR is set and +"$GIT_COMMON_DIR/objects" will be used instead. objects/[0-9a-f][0-9a-f]:: A newly created object is stored in its own file. @@ -92,7 +95,8 @@ refs:: References are stored in subdirectories of this directory. The 'git prune' command knows to preserve objects reachable from refs found in this directory and - its subdirectories. + its subdirectories. This directory is ignored if $GIT_COMMON_DIR + is set and "$GIT_COMMON_DIR/refs" will be used instead. refs/heads/`name`:: records tip-of-the-tree commit objects of branch `name` @@ -114,7 +118,8 @@ refs/replace/``:: packed-refs:: records the same information as refs/heads/, refs/tags/, and friends record in a more efficient way. See - linkgit:git-pack-refs[1]. + linkgit:git-pack-refs[1]. This file is ignored if $GIT_COMMON_DIR + is set and "$GIT_COMMON_DIR/packed-refs" will be used instead. HEAD:: A symref (see glossary) to the `refs/heads/` namespace @@ -133,6 +138,11 @@ being a symref to point at the current branch. Such a state is often called 'detached HEAD.' See linkgit:git-checkout[1] for details. +config:: + Repository specific configuration file. This file is ignored + if $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/config" will be + used instead. + branches:: A slightly deprecated way to s
[PATCH 09/34] fast-import: use git_path() for accessing .git dir instead of get_git_dir()
This allows git_path() to redirect info/fast-import to another place if needed Signed-off-by: Nguyễn Thái Ngọc Duy --- fast-import.c | 5 + 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/fast-import.c b/fast-import.c index 3018130..1b50923 100644 --- a/fast-import.c +++ b/fast-import.c @@ -3112,12 +3112,9 @@ static void parse_progress(void) static char* make_fast_import_path(const char *path) { - struct strbuf abs_path = STRBUF_INIT; - if (!relative_marks_paths || is_absolute_path(path)) return xstrdup(path); - strbuf_addf(&abs_path, "%s/info/fast-import/%s", get_git_dir(), path); - return strbuf_detach(&abs_path, NULL); + return xstrdup(git_path("info/fast-import/%s", path)); } static void option_import_marks(const char *marks, -- 2.1.0.rc0.78.gc0d8480 -- 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 08/34] reflog: avoid constructing .lock path with git_path
Among pathnames in $GIT_DIR, e.g. "index" or "packed-refs", we want to automatically and silently map some of them to the $GIT_DIR of the repository we are borrowing from via $GIT_COMMON_DIR mechanism. When we formulate the pathname for its lockfile, we want it to be in the same location as its final destination. "index" is not shared and needs to remain in the borrowing repository, while "packed-refs" is shared and needs to go to the borrowed repository. git_path() could be taught about the ".lock" suffix and map "index.lock" and "packed-refs.lock" the same way their basenames are mapped, but instead the caller can help by asking where the basename (e.g. "index") is mapped to git_path() and then appending ".lock" after the mapping is done. Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/reflog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/reflog.c b/builtin/reflog.c index 2d85d26..fb07c66 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -372,7 +372,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, if (!reflog_exists(ref)) goto finish; if (!cmd->dry_run) { - newlog_path = git_pathdup("logs/%s.lock", ref); + newlog_path = mkpathdup("%s.lock", log_file); cb.newlog = fopen(newlog_path, "w"); } -- 2.1.0.rc0.78.gc0d8480 -- 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 07/34] *.sh: respect $GIT_INDEX_FILE
Signed-off-by: Nguyễn Thái Ngọc Duy --- git-pull.sh | 2 +- git-stash.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/git-pull.sh b/git-pull.sh index 4d4fc77..ad44226 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -240,7 +240,7 @@ test true = "$rebase" && { if ! git rev-parse -q --verify HEAD >/dev/null then # On an unborn branch - if test -f "$GIT_DIR/index" + if test -f "$(git rev-parse --git-path index)" then die "$(gettext "updating an unborn branch with changes added to the index")" fi diff --git a/git-stash.sh b/git-stash.sh index d4cf818..b6d4b06 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -20,7 +20,7 @@ require_work_tree cd_to_toplevel TMP="$GIT_DIR/.git-stash.$$" -TMPindex=${GIT_INDEX_FILE-"$GIT_DIR/index"}.stash.$$ +TMPindex=${GIT_INDEX_FILE-"$(git rev-parse --git-path index)"}.stash.$$ trap 'rm -f "$TMP-"* "$TMPindex"' 0 ref_stash=refs/stash -- 2.1.0.rc0.78.gc0d8480 -- 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 06/34] git_path(): be aware of file relocation in $GIT_DIR
We allow the user to relocate certain paths out of $GIT_DIR via environment variables, e.g. GIT_OBJECT_DIRECTORY, GIT_INDEX_FILE and GIT_GRAFT_FILE. Callers are not supposed to use git_path() or git_pathdup() to get those paths. Instead they must use get_object_directory(), get_index_file() and get_graft_file() respectively. This is inconvenient and could be missed in review (for example, there's git_path("objects/info/alternates") somewhere in sha1_file.c). This patch makes git_path() and git_pathdup() understand those environment variables. So if you set GIT_OBJECT_DIRECTORY to /foo/bar, git_path("objects/abc") should return /foo/bar/abc. The same is done for the two remaining env variables. "git rev-parse --git-path" is the wrapper for script use. This patch kinda reverts a0279e1 (setup_git_env: use git_pathdup instead of xmalloc + sprintf - 2014-06-19) because using git_pathdup here would result in infinite recursion: setup_git_env() -> git_pathdup("objects") -> .. -> adjust_git_path() -> get_object_directory() -> oops, git_object_directory is NOT set yet -> setup_git_env() I wanted to make git_pathdup_literal() that skips adjust_git_path(). But that won't work because later on when $GIT_COMMON_DIR is introduced, git_pathdup_literal("objects") needs adjust_git_path() to replace $GIT_DIR with $GIT_COMMON_DIR. Signed-off-by: Nguyễn Thái Ngọc Duy --- Documentation/git-rev-parse.txt | 7 ++ builtin/rev-parse.c | 7 ++ cache.h | 1 + environment.c | 19 +++- path.c | 49 +++-- t/t0060-path-utils.sh | 19 6 files changed, 95 insertions(+), 7 deletions(-) diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index d6de42f..2103bbf 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -233,6 +233,13 @@ print a message to stderr and exit with nonzero status. repository. If is a gitfile then the resolved path to the real repository is printed. +--git-path :: + Resolve "$GIT_DIR/" and takes other path relocation + variables such as $GIT_OBJECT_DIRECTORY, + $GIT_INDEX_FILE... into account. For example, if + $GIT_OBJECT_DIRECTORY is set to /foo/bar then "git rev-parse + --git-path objects/abc" returns /foo/bar/abc. + --show-cdup:: When the command is invoked from a subdirectory, show the path of the top-level directory relative to the current diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 35d3c43..4cd44aa 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -533,6 +533,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) for (i = 1; i < argc; i++) { const char *arg = argv[i]; + if (!strcmp(arg, "--git-path")) { + if (!argv[i + 1]) + die("--git-path requires an argument"); + puts(git_path("%s", argv[i + 1])); + i++; + continue; + } if (as_is) { if (show_file(arg, output_prefix) && as_is < 2) verify_filename(prefix, arg, 0); diff --git a/cache.h b/cache.h index 56643cf..7da2ffb 100644 --- a/cache.h +++ b/cache.h @@ -617,6 +617,7 @@ extern int fsync_object_files; extern int core_preload_index; extern int core_apply_sparse_checkout; extern int precomposed_unicode; +extern int git_db_env, git_index_env, git_graft_env; /* * The character that begins a commented line in user-editable file diff --git a/environment.c b/environment.c index 565f652..fee12a6 100644 --- a/environment.c +++ b/environment.c @@ -83,6 +83,7 @@ static size_t namespace_len; static const char *git_dir; static char *git_object_dir, *git_index_file, *git_graft_file; +int git_db_env, git_index_env, git_graft_env; /* * Repository-local GIT_* environment variables; see cache.h for details. @@ -124,10 +125,18 @@ static char *expand_namespace(const char *raw_namespace) return strbuf_detach(&buf, NULL); } -static char *git_path_from_env(const char *envvar, const char *path) +static char *git_path_from_env(const char *envvar, const char *path, + int *fromenv) { const char *value = getenv(envvar); - return value ? xstrdup(value) : git_pathdup("%s", path); + if (!value) { + char *buf = xmalloc(strlen(git_dir) + strlen(path) + 2); + sprintf(buf, "%s/%s", git_dir, path); + return buf; + } + if (fromenv) + *fromenv = 1; + return xstrdup(value); } static void setup_git_env(void) @@ -140,9 +149,9 @@ static void setup_git_env(void) git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; gitfile = read_gitfile(git_dir);
[PATCH 05/34] path.c: group git_path(), git_pathdup() and strbuf_git_path() together
Signed-off-by: Nguyễn Thái Ngọc Duy --- path.c | 20 ++-- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/path.c b/path.c index 6991103..df0f75b 100644 --- a/path.c +++ b/path.c @@ -78,6 +78,16 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...) va_end(args); } +const char *git_path(const char *fmt, ...) +{ + struct strbuf *pathname = get_pathname(); + va_list args; + va_start(args, fmt); + do_git_path(pathname, fmt, args); + va_end(args); + return pathname->buf; +} + char *git_pathdup(const char *fmt, ...) { struct strbuf path = STRBUF_INIT; @@ -109,16 +119,6 @@ const char *mkpath(const char *fmt, ...) return cleanup_path(pathname->buf); } -const char *git_path(const char *fmt, ...) -{ - struct strbuf *pathname = get_pathname(); - va_list args; - va_start(args, fmt); - do_git_path(pathname, fmt, args); - va_end(args); - return pathname->buf; -} - void home_config_paths(char **global, char **xdg, char *file) { char *xdg_home = getenv("XDG_CONFIG_HOME"); -- 2.1.0.rc0.78.gc0d8480 -- 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 04/34] path.c: rename vsnpath() to do_git_path()
The name vsnpath() gives an impression that this is general path handling function. It's not. This is the underlying implementation of git_path(), git_pathdup() and strbuf_git_path() which will prefix $GIT_DIR in the result string. Signed-off-by: Nguyễn Thái Ngọc Duy --- path.c | 8 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/path.c b/path.c index 47753aa..6991103 100644 --- a/path.c +++ b/path.c @@ -60,7 +60,7 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...) return cleanup_path(buf); } -static void vsnpath(struct strbuf *buf, const char *fmt, va_list args) +static void do_git_path(struct strbuf *buf, const char *fmt, va_list args) { const char *git_dir = get_git_dir(); strbuf_addstr(buf, git_dir); @@ -74,7 +74,7 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...) { va_list args; va_start(args, fmt); - vsnpath(sb, fmt, args); + do_git_path(sb, fmt, args); va_end(args); } @@ -83,7 +83,7 @@ char *git_pathdup(const char *fmt, ...) struct strbuf path = STRBUF_INIT; va_list args; va_start(args, fmt); - vsnpath(&path, fmt, args); + do_git_path(&path, fmt, args); va_end(args); return strbuf_detach(&path, NULL); } @@ -114,7 +114,7 @@ const char *git_path(const char *fmt, ...) struct strbuf *pathname = get_pathname(); va_list args; va_start(args, fmt); - vsnpath(pathname, fmt, args); + do_git_path(pathname, fmt, args); va_end(args); return pathname->buf; } -- 2.1.0.rc0.78.gc0d8480 -- 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 00/34] nd/multiple-work-trees reroll
This is rebased so the diff below (against the version on Junio's repo) is only approximate. Changes include test fixes for Windows port, $GIT_COMMON_DIR and $GIT_DIR/modules problems with submodules. Patch 03/34 is rewritten to touch less in refs.c to reduce conflicts. A lot of changes there are just revert. diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt index 2b30a92..7173b38 100644 --- a/Documentation/gitrepository-layout.txt +++ b/Documentation/gitrepository-layout.txt @@ -248,9 +248,7 @@ commondir:: incomplete without the repository pointed by "commondir". modules:: - Contains the git-repositories of the submodules. This - directory is ignored if $GIT_COMMON_DIR is set and - "$GIT_COMMON_DIR/modules" will be used instead. + Contains the git-repositories of the submodules. worktrees:: Contains worktree specific information of linked diff --git a/builtin/checkout.c b/builtin/checkout.c index 01d0f2f..e70e66c 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -865,7 +865,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts, if (!new->commit) die(_("no branch specified")); - if (file_exists(path)) + if (file_exists(path) && !is_empty_dir(path)) die(_("'%s' already exists"), path); len = strlen(path); @@ -1207,7 +1207,7 @@ static int parse_branchname_arg(int argc, const char **argv, if (new->path && !force_detach && !*new_branch) { unsigned char sha1[20]; int flag; - char *head_ref = resolve_refdup("HEAD", sha1, 0, &flag); + char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag); if (head_ref && (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path))) check_linked_checkouts(new); diff --git a/environment.c b/environment.c index d5b0788..8351007 100644 --- a/environment.c +++ b/environment.c @@ -101,6 +101,7 @@ const char * const local_repo_env[] = { NO_REPLACE_OBJECTS_ENVIRONMENT, GIT_PREFIX_ENVIRONMENT, GIT_SHALLOW_FILE_ENVIRONMENT, + GIT_COMMON_DIR_ENVIRONMENT, NULL }; diff --git a/path.c b/path.c index 35d498e..a5c51a3 100644 --- a/path.c +++ b/path.c @@ -92,7 +92,7 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir) } static const char *common_list[] = { - "/branches", "/hooks", "/info", "!/logs", "/lost-found", "/modules", + "/branches", "/hooks", "/info", "!/logs", "/lost-found", "/objects", "/refs", "/remotes", "/worktrees", "/rr-cache", "/svn", "config", "!gc.pid", "packed-refs", "shallow", NULL diff --git a/refs.c b/refs.c index 3cefbd3..f7e48b0 100644 --- a/refs.c +++ b/refs.c @@ -1398,14 +1398,16 @@ static const char *handle_missing_loose_ref(const char *refname, } /* This function needs to return a meaningful errno on failure */ -const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag) +static const char *resolve_ref_unsafe_1(const char *refname, + unsigned char *sha1, + int reading, + int *flags, + struct strbuf *sb_path) { - struct strbuf sb_path = STRBUF_INIT; int depth = MAXDEPTH; ssize_t len; char buffer[256]; static char refname_buffer[256]; - const char *ret; if (flag) *flag = 0; @@ -1423,12 +1425,12 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea if (--depth < 0) { errno = ELOOP; - goto fail; + return NULL; } - strbuf_reset(&sb_path); - strbuf_git_path(&sb_path, "%s", refname); - path = sb_path.buf; + strbuf_reset(sb_path); + strbuf_git_path(sb_path, "%s", refname); + path = sb_path->buf; /* * We might have to loop back here to avoid a race @@ -1442,11 +1444,10 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea stat_ref: if (lstat(path, &st) < 0) { if (errno == ENOENT) - ret = handle_missing_loose_ref(refname, sha1, - reading, flag); + return handle_missing_loose_ref(refname, sha1, + reading, flag); else - ret = NULL; - goto done; + return NULL; } /* Foll
[PATCH 02/34] path.c: make get_pathname() call sites return const char *
Before the previous commit, get_pathname returns an array of PATH_MAX length. Even if git_path() and similar functions does not use the whole array, git_path() caller can, in theory. After the commit, get_pathname() may return a buffer that has just enough room for the returned string and git_path() caller should never write beyond that. Make git_path(), mkpath() and git_path_submodule() return a const buffer to make sure callers do not write in it at all. This could have been part of the previous commit, but the "const" conversion is too much distraction from the core changes in path.c. Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/checkout.c | 2 +- builtin/clone.c| 9 + builtin/fetch.c| 5 +++-- builtin/fsck.c | 4 ++-- builtin/receive-pack.c | 2 +- builtin/remote.c | 2 +- builtin/repack.c | 8 +--- cache.h| 6 +++--- fast-import.c | 2 +- notes-merge.c | 6 +++--- path.c | 6 +++--- refs.c | 8 run-command.c | 4 ++-- run-command.h | 2 +- sha1_file.c| 2 +- 15 files changed, 36 insertions(+), 32 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 5410dac..c600ec1 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -589,7 +589,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts, if (opts->new_branch_log && !log_all_ref_updates) { int temp; char log_file[PATH_MAX]; - char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch); + const char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch); temp = log_all_ref_updates; log_all_ref_updates = 1; diff --git a/builtin/clone.c b/builtin/clone.c index d5e7532..a9af3f2 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -290,16 +290,17 @@ static void copy_alternates(struct strbuf *src, struct strbuf *dst, struct strbuf line = STRBUF_INIT; while (strbuf_getline(&line, in, '\n') != EOF) { - char *abs_path, abs_buf[PATH_MAX]; + char *abs_path; if (!line.len || line.buf[0] == '#') continue; if (is_absolute_path(line.buf)) { add_to_alternates_file(line.buf); continue; } - abs_path = mkpath("%s/objects/%s", src_repo, line.buf); - normalize_path_copy(abs_buf, abs_path); - add_to_alternates_file(abs_buf); + abs_path = mkpathdup("%s/objects/%s", src_repo, line.buf); + normalize_path_copy(abs_path, abs_path); + add_to_alternates_file(abs_path); + free(abs_path); } strbuf_release(&line); fclose(in); diff --git a/builtin/fetch.c b/builtin/fetch.c index 7b84d35..cb54936 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -587,7 +587,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, struct strbuf note = STRBUF_INIT; const char *what, *kind; struct ref *rm; - char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD"); + char *url; + const char *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD"); int want_status; fp = fopen(filename, "a"); @@ -821,7 +822,7 @@ static void check_not_current_branch(struct ref *ref_map) static int truncate_fetch_head(void) { - char *filename = git_path("FETCH_HEAD"); + const char *filename = git_path("FETCH_HEAD"); FILE *fp = fopen(filename, "w"); if (!fp) diff --git a/builtin/fsck.c b/builtin/fsck.c index a27515a..b92aeff 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -225,12 +225,12 @@ static void check_unreachable_object(struct object *obj) printf("dangling %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1)); if (write_lost_and_found) { - char *filename = git_path("lost-found/%s/%s", + const char *filename = git_path("lost-found/%s/%s", obj->type == OBJ_COMMIT ? "commit" : "other", sha1_to_hex(obj->sha1)); FILE *f; - if (safe_create_leading_directories(filename)) { + if (safe_create_leading_directories_const(filename)) { error("Could not create lost-found"); return; } diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 32fc540..3b8f420 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -869,7 +869,7 @@ st
[PATCH 03/34] git_snpath(): retire and replace with strbuf_git_path()
In the previous patch, git_snpath() is modified to allocate a new strbuf buffer because vsnpath() needs that. But that makes it awkward because git_snpath() receives a pre-allocated buffer from outside and has to copy data back. Rename it to strbuf_git_path() and make it receive strbuf directly. Using git_path() in update_refs_for_switch() which used to call git_snpath() is safe because that function and all of its callers do not keep any pointer to the round-robin buffer pool allocated by get_pathname(). Signed-off-by: Nguyễn Thái Ngọc Duy --- builtin/checkout.c | 13 - cache.h| 4 ++-- path.c | 11 ++- refs.c | 51 ++- refs.h | 2 +- 5 files changed, 55 insertions(+), 26 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index c600ec1..195aca7 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -588,18 +588,21 @@ static void update_refs_for_switch(const struct checkout_opts *opts, if (opts->new_orphan_branch) { if (opts->new_branch_log && !log_all_ref_updates) { int temp; - char log_file[PATH_MAX]; - const char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch); + struct strbuf log_file = STRBUF_INIT; + int ret; + const char *ref_name; + ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch); temp = log_all_ref_updates; log_all_ref_updates = 1; - if (log_ref_setup(ref_name, log_file, sizeof(log_file))) { + ret = log_ref_setup(ref_name, &log_file); + log_all_ref_updates = temp; + strbuf_release(&log_file); + if (ret) { fprintf(stderr, _("Can not do reflog for '%s'\n"), opts->new_orphan_branch); - log_all_ref_updates = temp; return; } - log_all_ref_updates = temp; } } else diff --git a/cache.h b/cache.h index dbee0a3..56643cf 100644 --- a/cache.h +++ b/cache.h @@ -679,8 +679,8 @@ extern int check_repository_format(void); extern char *mksnpath(char *buf, size_t n, const char *fmt, ...) __attribute__((format (printf, 3, 4))); -extern char *git_snpath(char *buf, size_t n, const char *fmt, ...) - __attribute__((format (printf, 3, 4))); +extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...) + __attribute__((format (printf, 2, 3))); extern char *git_pathdup(const char *fmt, ...) __attribute__((format (printf, 1, 2))); extern char *mkpathdup(const char *fmt, ...) diff --git a/path.c b/path.c index a7ceea2..47753aa 100644 --- a/path.c +++ b/path.c @@ -70,19 +70,12 @@ static void vsnpath(struct strbuf *buf, const char *fmt, va_list args) strbuf_cleanup_path(buf); } -char *git_snpath(char *buf, size_t n, const char *fmt, ...) +void strbuf_git_path(struct strbuf *sb, const char *fmt, ...) { - struct strbuf sb = STRBUF_INIT; va_list args; va_start(args, fmt); - vsnpath(&sb, fmt, args); + vsnpath(sb, fmt, args); va_end(args); - if (sb.len >= n) - strlcpy(buf, bad_path, n); - else - memcpy(buf, sb.buf, sb.len + 1); - strbuf_release(&sb); - return buf; } char *git_pathdup(const char *fmt, ...) diff --git a/refs.c b/refs.c index 23617e0..5ed991b 100644 --- a/refs.c +++ b/refs.c @@ -1444,7 +1444,11 @@ static int resolve_missing_loose_ref(const char *refname, } /* This function needs to return a meaningful errno on failure */ -const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned char *sha1, int *flags) +static const char *resolve_ref_unsafe_1(const char *refname, + int resolve_flags, + unsigned char *sha1, + int *flags, + struct strbuf *sb_path) { int depth = MAXDEPTH; ssize_t len; @@ -1475,7 +1479,7 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned bad_name = 1; } for (;;) { - char path[PATH_MAX]; + const char *path; struct stat st; char *buf; int fd; @@ -1485,7 +1489,9 @@ const char *resolve_ref_unsafe(const char *refname, int resol
[PATCH 01/34] path.c: make get_pathname() return strbuf instead of static buffer
We've been avoiding PATH_MAX whenever possible. This patch makes get_pathname() return a strbuf and updates the callers to take advantage of this. The code is simplified as we no longer need to worry about buffer overflow. vsnpath() behavior is changed slightly: previously it always clears the buffer before writing, now it just appends. Fortunately this is a static function and all of its callers prepare the buffer properly: git_path() gets the buffer from get_pathname() which resets the buffer, the remaining call sites start with STRBUF_INIT'd buffer. Signed-off-by: Nguyễn Thái Ngọc Duy --- path.c | 120 - 1 file changed, 51 insertions(+), 69 deletions(-) diff --git a/path.c b/path.c index f68df0c..015c0e4 100644 --- a/path.c +++ b/path.c @@ -16,11 +16,15 @@ static int get_st_mode_bits(const char *path, int *mode) static char bad_path[] = "/bad-path/"; -static char *get_pathname(void) +static struct strbuf *get_pathname(void) { - static char pathname_array[4][PATH_MAX]; + static struct strbuf pathname_array[4] = { + STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT + }; static int index; - return pathname_array[3 & ++index]; + struct strbuf *sb = &pathname_array[3 & ++index]; + strbuf_reset(sb); + return sb; } static char *cleanup_path(char *path) @@ -34,6 +38,13 @@ static char *cleanup_path(char *path) return path; } +static void strbuf_cleanup_path(struct strbuf *sb) +{ + char *path = cleanup_path(sb->buf); + if (path > sb->buf) + strbuf_remove(sb, 0, path - sb->buf); +} + char *mksnpath(char *buf, size_t n, const char *fmt, ...) { va_list args; @@ -49,85 +60,70 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...) return cleanup_path(buf); } -static char *vsnpath(char *buf, size_t n, const char *fmt, va_list args) +static void vsnpath(struct strbuf *buf, const char *fmt, va_list args) { const char *git_dir = get_git_dir(); - size_t len; - - len = strlen(git_dir); - if (n < len + 1) - goto bad; - memcpy(buf, git_dir, len); - if (len && !is_dir_sep(git_dir[len-1])) - buf[len++] = '/'; - len += vsnprintf(buf + len, n - len, fmt, args); - if (len >= n) - goto bad; - return cleanup_path(buf); -bad: - strlcpy(buf, bad_path, n); - return buf; + strbuf_addstr(buf, git_dir); + if (buf->len && !is_dir_sep(buf->buf[buf->len - 1])) + strbuf_addch(buf, '/'); + strbuf_vaddf(buf, fmt, args); + strbuf_cleanup_path(buf); } char *git_snpath(char *buf, size_t n, const char *fmt, ...) { - char *ret; + struct strbuf sb = STRBUF_INIT; va_list args; va_start(args, fmt); - ret = vsnpath(buf, n, fmt, args); + vsnpath(&sb, fmt, args); va_end(args); - return ret; + if (sb.len >= n) + strlcpy(buf, bad_path, n); + else + memcpy(buf, sb.buf, sb.len + 1); + strbuf_release(&sb); + return buf; } char *git_pathdup(const char *fmt, ...) { - char path[PATH_MAX], *ret; + struct strbuf path = STRBUF_INIT; va_list args; va_start(args, fmt); - ret = vsnpath(path, sizeof(path), fmt, args); + vsnpath(&path, fmt, args); va_end(args); - return xstrdup(ret); + return strbuf_detach(&path, NULL); } char *mkpathdup(const char *fmt, ...) { - char *path; struct strbuf sb = STRBUF_INIT; va_list args; - va_start(args, fmt); strbuf_vaddf(&sb, fmt, args); va_end(args); - path = xstrdup(cleanup_path(sb.buf)); - - strbuf_release(&sb); - return path; + strbuf_cleanup_path(&sb); + return strbuf_detach(&sb, NULL); } char *mkpath(const char *fmt, ...) { va_list args; - unsigned len; - char *pathname = get_pathname(); - + struct strbuf *pathname = get_pathname(); va_start(args, fmt); - len = vsnprintf(pathname, PATH_MAX, fmt, args); + strbuf_vaddf(pathname, fmt, args); va_end(args); - if (len >= PATH_MAX) - return bad_path; - return cleanup_path(pathname); + return cleanup_path(pathname->buf); } char *git_path(const char *fmt, ...) { - char *pathname = get_pathname(); + struct strbuf *pathname = get_pathname(); va_list args; - char *ret; - va_start(args, fmt); - ret = vsnpath(pathname, PATH_MAX, fmt, args); + vsnpath(pathname, fmt, args); va_end(args); - return ret; + return pathname->buf; } void home_config_paths(char **global, char **xdg, char *file) @@ -160,41 +156,27 @@ void home_config_paths(char **global, char **xdg, char *file) char *git_path_submodule(const char *path, const char *fmt