From 531220ba500168bc5a2080df24dfd61705cafa3c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 17 Mar 2016 22:40:05 -0700 Subject: send-email: detect and offer to skip backup files Diligent people save output from format-patch to files, proofread and edit them and then finally send the result out. If the resulting files are sent out with "git send-email 0*", this ends up sending backup files (e.g. 0001-X.patch.backup or 0001-X.patch~) left by their editors next to the final version. Sending them with "git send-email 0*.patch" (if format-patch was run with the standard suffix) would avoid such an embarrassment, but not everybody is careful. After collecting files to be sent (and sorting them if read from a directory), notice when the file being sent out has the same name as the previous file, plus some suffix (e.g. 0001-X.patch was sent, and we are looking at 0001-X.patch.backup or 0001-X.patch~), and the suffix begins with a non-alnum (e.g. ".backup" or "~") and ask if the user really wants to send it out. Once the user skips sending such a "backup" file, remember the suffix and stop asking the same question (e.g. after skipping 0001-X.patch~, skip 0002-Y.patch~ without asking). Signed-off-by: Junio C Hamano --- git-send-email.perl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/git-send-email.perl b/git-send-email.perl index d356901348..b1be698e21 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -621,6 +621,8 @@ if (@rev_list_opts) { push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts); } +@files = handle_backup_files(@files); + if ($validate) { foreach my $f (@files) { unless (-p $f) { @@ -1726,6 +1728,44 @@ sub validate_patch { return; } +sub handle_backup { + my ($last, $lastlen, $file, $known_suffix) = @_; + my ($suffix, $skip); + + $skip = 0; + if (defined $last && + ($lastlen < length($file)) && + (substr($file, 0, $lastlen) eq $last) && + ($suffix = substr($file, $lastlen)) !~ /^[a-z0-9]/i) { + if (defined $known_suffix && $suffix eq $known_suffix) { + print "Skipping $file with backup suffix '$known_suffix'.\n"; + $skip = 1; + } else { + my $answer = ask("Do you really want to send $file? (y|N): ", + valid_re => qr/^(?:y|n)/i, + default => 'n'); + $skip = ($answer ne 'y'); + if ($skip) { + $known_suffix = $suffix; + } + } + } + return ($skip, $known_suffix); +} + +sub handle_backup_files { + my @file = @_; + my ($last, $lastlen, $known_suffix, $skip, @result); + for my $file (@file) { + ($skip, $known_suffix) = handle_backup($last, $lastlen, + $file, $known_suffix); + push @result, $file unless $skip; + $last = $file; + $lastlen = length($file); + } + return @result; +} + sub file_has_nonascii { my $fn = shift; open(my $fh, '<', $fn) -- cgit v1.2.1 From 19dd7d06e5d2c58895dd101025c013404025e192 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 5 May 2016 13:22:23 +0200 Subject: t1404: demonstrate a bug resolving references Add some tests checking that it is possible to work with a reference even if there is an empty directory where the loose ref would be stored. One of the new tests demonstrates a bug that has been with us since at least 2.5.0--single reference lookup gives up when it sees the directory, even if the reference exists as a packed ref. This probably hasn't been reported before because Git usually cleans up empty directories when packing references. This bug will be fixed shortly. Signed-off-by: Michael Haggerty --- t/t1404-update-ref-df-conflicts.sh | 76 +++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/t/t1404-update-ref-df-conflicts.sh b/t/t1404-update-ref-df-conflicts.sh index 66bafb5cf4..16752a999f 100755 --- a/t/t1404-update-ref-df-conflicts.sh +++ b/t/t1404-update-ref-df-conflicts.sh @@ -28,7 +28,9 @@ Q="'" test_expect_success 'setup' ' git commit --allow-empty -m Initial && - C=$(git rev-parse HEAD) + C=$(git rev-parse HEAD) && + git commit --allow-empty -m Second && + D=$(git rev-parse HEAD) ' @@ -104,4 +106,76 @@ test_expect_success 'one new ref is a simple prefix of another' ' ' +test_expect_failure 'empty directory should not fool rev-parse' ' + prefix=refs/e-rev-parse && + git update-ref $prefix/foo $C && + git pack-refs --all && + mkdir -p .git/$prefix/foo/bar/baz && + echo "$C" >expected && + git rev-parse $prefix/foo >actual && + test_cmp expected actual +' + +test_expect_success 'empty directory should not fool for-each-ref' ' + prefix=refs/e-for-each-ref && + git update-ref $prefix/foo $C && + git for-each-ref $prefix >expected && + git pack-refs --all && + mkdir -p .git/$prefix/foo/bar/baz && + git for-each-ref $prefix >actual && + test_cmp expected actual +' + +test_expect_success 'empty directory should not fool create' ' + prefix=refs/e-create && + mkdir -p .git/$prefix/foo/bar/baz && + printf "create %s $C\n" $prefix/foo | + git update-ref --stdin +' + +test_expect_success 'empty directory should not fool verify' ' + prefix=refs/e-verify && + git update-ref $prefix/foo $C && + git pack-refs --all && + mkdir -p .git/$prefix/foo/bar/baz && + printf "verify %s $C\n" $prefix/foo | + git update-ref --stdin +' + +test_expect_success 'empty directory should not fool 1-arg update' ' + prefix=refs/e-update-1 && + git update-ref $prefix/foo $C && + git pack-refs --all && + mkdir -p .git/$prefix/foo/bar/baz && + printf "update %s $D\n" $prefix/foo | + git update-ref --stdin +' + +test_expect_success 'empty directory should not fool 2-arg update' ' + prefix=refs/e-update-2 && + git update-ref $prefix/foo $C && + git pack-refs --all && + mkdir -p .git/$prefix/foo/bar/baz && + printf "update %s $D $C\n" $prefix/foo | + git update-ref --stdin +' + +test_expect_success 'empty directory should not fool 0-arg delete' ' + prefix=refs/e-delete-0 && + git update-ref $prefix/foo $C && + git pack-refs --all && + mkdir -p .git/$prefix/foo/bar/baz && + printf "delete %s\n" $prefix/foo | + git update-ref --stdin +' + +test_expect_success 'empty directory should not fool 1-arg delete' ' + prefix=refs/e-delete-1 && + git update-ref $prefix/foo $C && + git pack-refs --all && + mkdir -p .git/$prefix/foo/bar/baz && + printf "delete %s $C\n" $prefix/foo | + git update-ref --stdin +' + test_done -- cgit v1.2.1 From 5387c0d8839e366c44838c808ccc20eb7f9bd358 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 5 May 2016 15:33:03 +0200 Subject: commit_ref(): if there is an empty dir in the way, delete it Part of the bug revealed in the last commit is that resolve_ref_unsafe() incorrectly returns EISDIR if it finds a directory in the place where it is looking for a loose reference, even if the corresponding packed reference exists. lock_ref_sha1_basic() notices the bogus EISDIR, and use it as an indication that it should call remove_empty_directories() and call resolve_ref_unsafe() again. But resolve_ref_unsafe() shouldn't report EISDIR in this case. If we would simply make that change, then remove_empty_directories() wouldn't get called anymore, and the empty directory would get in the way when commit_ref() calls commit_lock_file() to rename the lockfile into place. So instead of relying on lock_ref_sha1_basic() to delete empty directories, teach commit_ref(), just before calling commit_lock_file(), to check whether a directory is in the way, and if so, try to delete it. Signed-off-by: Michael Haggerty --- refs/files-backend.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/refs/files-backend.c b/refs/files-backend.c index 1f38076411..ad9cd86457 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2457,6 +2457,30 @@ static int close_ref(struct ref_lock *lock) static int commit_ref(struct ref_lock *lock) { + char *path = get_locked_file_path(lock->lk); + struct stat st; + + if (!lstat(path, &st) && S_ISDIR(st.st_mode)) { + /* + * There is a directory at the path we want to rename + * the lockfile to. Hopefully it is empty; try to + * delete it. + */ + size_t len = strlen(path); + struct strbuf sb_path = STRBUF_INIT; + + strbuf_attach(&sb_path, path, len, len); + + /* + * If this fails, commit_lock_file() will also fail + * and will report the problem. + */ + remove_empty_directories(&sb_path); + strbuf_release(&sb_path); + } else { + free(path); + } + if (commit_lock_file(lock->lk)) return -1; return 0; -- cgit v1.2.1 From e167a5673e25b960dce118fb967d54da30b69def Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 5 May 2016 14:09:41 +0200 Subject: read_raw_ref(): don't get confused by an empty directory Even if there is an empty directory where we look for the loose version of a reference, check for a packed reference before giving up. This fixes the failing test that was introduced two commits ago. Signed-off-by: Michael Haggerty --- refs/files-backend.c | 11 ++++++++++- t/t1404-update-ref-df-conflicts.sh | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index ad9cd86457..0cc116d67c 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1477,7 +1477,16 @@ stat_ref: /* Is it a directory? */ if (S_ISDIR(st.st_mode)) { - errno = EISDIR; + /* + * Even though there is a directory where the loose + * ref is supposed to be, there could still be a + * packed ref: + */ + if (resolve_missing_loose_ref(refname, sha1, flags)) { + errno = EISDIR; + goto out; + } + ret = 0; goto out; } diff --git a/t/t1404-update-ref-df-conflicts.sh b/t/t1404-update-ref-df-conflicts.sh index 16752a999f..6d869d1285 100755 --- a/t/t1404-update-ref-df-conflicts.sh +++ b/t/t1404-update-ref-df-conflicts.sh @@ -106,7 +106,7 @@ test_expect_success 'one new ref is a simple prefix of another' ' ' -test_expect_failure 'empty directory should not fool rev-parse' ' +test_expect_success 'empty directory should not fool rev-parse' ' prefix=refs/e-rev-parse && git update-ref $prefix/foo $C && git pack-refs --all && -- cgit v1.2.1 From e95792e532bde75fd4a1e91aecfcf9a28ba23955 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Sun, 24 Apr 2016 04:34:12 +0200 Subject: safe_create_leading_directories(): improve docstring Document the difference between this function and safe_create_leading_directories_const(), and that the former restores path before returning. Signed-off-by: Michael Haggerty --- cache.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cache.h b/cache.h index 2711048cad..4134f648ec 100644 --- a/cache.h +++ b/cache.h @@ -993,6 +993,11 @@ int adjust_shared_perm(const char *path); * directory while we were working. To be robust against this kind of * race, callers might want to try invoking the function again when it * returns SCLD_VANISHED. + * + * safe_create_leading_directories() temporarily changes path while it + * is working but restores it before returning. + * safe_create_leading_directories_const() doesn't modify path, even + * temporarily. */ enum scld_error { SCLD_OK = 0, -- cgit v1.2.1 From 728af2832c3e58222965521682414adb9a80932b Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Sun, 24 Apr 2016 07:07:59 +0200 Subject: remove_dir_recursively(): add docstring Add a docstring for the remove_dir_recursively() function and the REMOVE_DIR_* flags that can be passed to it. Signed-off-by: Michael Haggerty --- dir.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/dir.h b/dir.h index 301b737a37..5f19acc272 100644 --- a/dir.h +++ b/dir.h @@ -262,9 +262,32 @@ extern int is_empty_dir(const char *dir); extern void setup_standard_excludes(struct dir_struct *dir); + +/* Constants for remove_dir_recursively: */ + +/* + * If a non-directory is found within path, stop and return an error. + * (In this case some empty directories might already have been + * removed.) + */ #define REMOVE_DIR_EMPTY_ONLY 01 + +/* + * If any Git work trees are found within path, skip them without + * considering it an error. + */ #define REMOVE_DIR_KEEP_NESTED_GIT 02 + +/* Remove the contents of path, but leave path itself. */ #define REMOVE_DIR_KEEP_TOPLEVEL 04 + +/* + * Remove path and its contents, recursively. flags is a combination + * of the above REMOVE_DIR_* constants. Return 0 on success. + * + * This function uses path as temporary scratch space, but restores it + * before returning. + */ extern int remove_dir_recursively(struct strbuf *path, int flag); /* tries to remove the path with empty directories along it, ignores ENOENT */ -- cgit v1.2.1 From 39950fef8bb45e944655e48393ee04c0b33211f5 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 27 Apr 2016 12:39:11 +0200 Subject: refname_is_safe(): use skip_prefix() Signed-off-by: Michael Haggerty --- refs.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/refs.c b/refs.c index 87dc82f1d8..5789152743 100644 --- a/refs.c +++ b/refs.c @@ -120,17 +120,19 @@ int check_refname_format(const char *refname, int flags) int refname_is_safe(const char *refname) { - if (starts_with(refname, "refs/")) { + const char *rest; + + if (skip_prefix(refname, "refs/", &rest)) { char *buf; int result; - buf = xmallocz(strlen(refname)); /* * Does the refname try to escape refs/? * For example: refs/foo/../bar is safe but refs/foo/../../bar * is not. */ - result = !normalize_path_copy(buf, refname + strlen("refs/")); + buf = xmallocz(strlen(rest)); + result = !normalize_path_copy(buf, rest); free(buf); return result; } -- cgit v1.2.1 From 35db25c65f6f77c153ef2b1183ea7821236201c8 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 27 Apr 2016 12:42:27 +0200 Subject: refname_is_safe(): don't allow the empty string Signed-off-by: Michael Haggerty --- refs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/refs.c b/refs.c index 5789152743..ca0280f7eb 100644 --- a/refs.c +++ b/refs.c @@ -136,11 +136,12 @@ int refname_is_safe(const char *refname) free(buf); return result; } - while (*refname) { + + do { if (!isupper(*refname) && *refname != '_') return 0; refname++; - } + } while (*refname); return 1; } -- cgit v1.2.1 From e40f3557f7e767bd2be2a824bc3bc2379aa69931 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 27 Apr 2016 12:40:39 +0200 Subject: refname_is_safe(): insist that the refname already be normalized The reference name is going to be compared to other reference names, so it should be in its normalized form. Signed-off-by: Michael Haggerty --- refs.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/refs.c b/refs.c index ca0280f7eb..b18d9959af 100644 --- a/refs.c +++ b/refs.c @@ -125,14 +125,19 @@ int refname_is_safe(const char *refname) if (skip_prefix(refname, "refs/", &rest)) { char *buf; int result; + size_t restlen = strlen(rest); + + /* rest must not be empty, or start or end with "/" */ + if (!restlen || *rest == '/' || rest[restlen - 1] == '/') + return 0; /* * Does the refname try to escape refs/? * For example: refs/foo/../bar is safe but refs/foo/../../bar * is not. */ - buf = xmallocz(strlen(rest)); - result = !normalize_path_copy(buf, rest); + buf = xmallocz(restlen); + result = !normalize_path_copy(buf, rest) && !strcmp(buf, rest); free(buf); return result; } -- cgit v1.2.1 From 76fc394d50efef8f1308a0f0d56087f502dac689 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Tue, 26 Apr 2016 09:09:51 +0200 Subject: commit_ref_update(): write error message to *err, not stderr Signed-off-by: Michael Haggerty --- refs/files-backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 0cc116d67c..2d3a8c669a 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2719,7 +2719,7 @@ static int commit_ref_update(struct ref_lock *lock, } } if (commit_ref(lock)) { - error("Couldn't set %s", lock->ref_name); + strbuf_addf(err, "Couldn't set %s", lock->ref_name); unlock_ref(lock); return -1; } -- cgit v1.2.1 From e711b1af2ead2ffad5c510aadbbc387c7d8aa4c7 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 21 Apr 2016 23:42:19 +0200 Subject: rename_ref(): remove unneeded local variable Signed-off-by: Michael Haggerty --- refs/files-backend.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 2d3a8c669a..80d346fd38 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2360,20 +2360,17 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms struct ref_lock *lock; struct stat loginfo; int log = !lstat(git_path("logs/%s", oldrefname), &loginfo); - const char *symref = NULL; struct strbuf err = STRBUF_INIT; if (log && S_ISLNK(loginfo.st_mode)) return error("reflog for %s is a symlink", oldrefname); - symref = resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING, - orig_sha1, &flag); + if (!resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING, orig_sha1, &flag)) + return error("refname %s not found", oldrefname); + if (flag & REF_ISSYMREF) return error("refname %s is a symbolic ref, renaming it is not supported", oldrefname); - if (!symref) - return error("refname %s not found", oldrefname); - if (!rename_ref_available(oldrefname, newrefname)) return 1; -- cgit v1.2.1 From d9545c7f465ed103df44cd93caddfdd265757779 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 25 Apr 2016 21:17:28 +0000 Subject: fast-import: implement unpack limit With many incremental imports, small packs become highly inefficient due to the need to readdir scan and load many indices to locate even a single object. Frequent repacking and consolidation may be prohibitively expensive in terms of disk I/O, especially in large repositories where the initial packs were aggressively optimized and marked with .keep files. In those cases, users may be better served with loose objects and relying on "git gc --auto". This changes the default behavior of fast-import for small imports found in test cases, so adjustments to t9300 were necessary. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- Documentation/config.txt | 9 +++++++ Documentation/git-fast-import.txt | 2 ++ fast-import.c | 32 +++++++++++++++++++++++++ t/t9300-fast-import.sh | 2 ++ t/t9302-fast-import-unpack-limit.sh | 48 +++++++++++++++++++++++++++++++++++++ 5 files changed, 93 insertions(+) create mode 100755 t/t9302-fast-import-unpack-limit.sh diff --git a/Documentation/config.txt b/Documentation/config.txt index 2cd6bdd7d2..283bf04091 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1153,6 +1153,15 @@ difftool..cmd:: difftool.prompt:: Prompt before each invocation of the diff tool. +fastimport.unpackLimit:: + If the number of objects imported by linkgit:git-fast-import[1] + is below this limit, then the objects will be unpacked into + loose object files. However if the number of imported objects + equals or exceeds this limit then the pack will be stored as a + pack. Storing the pack from a fast-import can make the import + operation complete faster, especially on slow filesystems. If + not set, the value of `transfer.unpackLimit` is used instead. + fetch.recurseSubmodules:: This option can be either set to a boolean value or to 'on-demand'. Setting it to a boolean changes the behavior of fetch and pull to diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt index 66910aa2fa..644df993f9 100644 --- a/Documentation/git-fast-import.txt +++ b/Documentation/git-fast-import.txt @@ -136,6 +136,8 @@ Performance and Compression Tuning Maximum size of each output packfile. The default is unlimited. +fastimport.unpackLimit:: + See linkgit:git-config[1] Performance ----------- diff --git a/fast-import.c b/fast-import.c index 9fc7093406..4fb464c1ef 100644 --- a/fast-import.c +++ b/fast-import.c @@ -166,6 +166,7 @@ Format of STDIN stream: #include "quote.h" #include "exec_cmd.h" #include "dir.h" +#include "run-command.h" #define PACK_ID_BITS 16 #define MAX_PACK_ID ((1<pack_fd, 0, SEEK_SET) < 0) + die_errno("Failed seeking to start of '%s'", p->pack_name); + + unpack.in = p->pack_fd; + unpack.git_cmd = 1; + unpack.stdout_to_stderr = 1; + argv_array_push(&unpack.args, "unpack-objects"); + if (!show_stats) + argv_array_push(&unpack.args, "-q"); + + return run_command(&unpack); +} + static void end_packfile(void) { static int running; @@ -972,6 +991,12 @@ static void end_packfile(void) fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1, pack_data->pack_name, object_count, cur_pack_sha1, pack_size); + + if (object_count <= unpack_limit) { + if (!loosen_small_pack(pack_data)) + goto discard_pack; + } + close(pack_data->pack_fd); idx_name = keep_pack(create_index()); @@ -1002,6 +1027,7 @@ static void end_packfile(void) pack_id++; } else { +discard_pack: close(pack_data->pack_fd); unlink_or_warn(pack_data->pack_name); } @@ -3317,6 +3343,7 @@ static void parse_option(const char *option) static void git_pack_config(void) { int indexversion_value; + int limit; unsigned long packsizelimit_value; if (!git_config_get_ulong("pack.depth", &max_depth)) { @@ -3341,6 +3368,11 @@ static void git_pack_config(void) if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value)) max_packsize = packsizelimit_value; + if (!git_config_get_int("fastimport.unpacklimit", &limit)) + unpack_limit = limit; + else if (!git_config_get_int("transfer.unpacklimit", &limit)) + unpack_limit = limit; + git_config(git_default_config, NULL); } diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index 25bb60b281..e6a2b8a4d7 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -52,6 +52,7 @@ echo "$@"' ### test_expect_success 'empty stream succeeds' ' + git config fastimport.unpackLimit 0 && git fast-import >input && test_create_repo R && + git --git-dir=R/.git config fastimport.unpackLimit 0 && git --git-dir=R/.git fast-import --big-file-threshold=1 input <<-INPUT_END && + commit refs/heads/master + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <input <<-INPUT_END && + commit refs/heads/master + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data < $GIT_COMMITTER_DATE + data < Date: Wed, 11 May 2016 15:16:12 +0200 Subject: builtin/apply: make gitdiff_verify_name() return void As the value returned by gitdiff_verify_name() is put into the same variable that is passed as a parameter to this function, it is simpler to pass the address of the variable and have gitdiff_verify_name() change the variable itself. This also makes it possible to later have this function return -1 instead of die()ing in case of error. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 8e4da2e1bd..fe5aebdb0f 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -925,43 +925,43 @@ static int gitdiff_hdrend(const char *line, struct patch *patch) #define DIFF_OLD_NAME 0 #define DIFF_NEW_NAME 1 -static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, int side) +static void gitdiff_verify_name(const char *line, int isnull, char **name, int side) { - if (!orig_name && !isnull) - return find_name(line, NULL, p_value, TERM_TAB); + if (!*name && !isnull) { + *name = find_name(line, NULL, p_value, TERM_TAB); + return; + } - if (orig_name) { - int len = strlen(orig_name); + if (*name) { + int len = strlen(*name); char *another; if (isnull) die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), - orig_name, linenr); + *name, linenr); another = find_name(line, NULL, p_value, TERM_TAB); - if (!another || memcmp(another, orig_name, len + 1)) + if (!another || memcmp(another, *name, len + 1)) die((side == DIFF_NEW_NAME) ? _("git apply: bad git-diff - inconsistent new filename on line %d") : _("git apply: bad git-diff - inconsistent old filename on line %d"), linenr); free(another); - return orig_name; } else { /* expect "/dev/null" */ if (memcmp("/dev/null", line, 9) || line[9] != '\n') die(_("git apply: bad git-diff - expected /dev/null on line %d"), linenr); - return NULL; } } static int gitdiff_oldname(const char *line, struct patch *patch) { - patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name, - DIFF_OLD_NAME); + gitdiff_verify_name(line, patch->is_new, &patch->old_name, + DIFF_OLD_NAME); return 0; } static int gitdiff_newname(const char *line, struct patch *patch) { - patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name, - DIFF_NEW_NAME); + gitdiff_verify_name(line, patch->is_delete, &patch->new_name, + DIFF_NEW_NAME); return 0; } -- cgit v1.2.1 From 560e35468feea3b471418dfe48d753f433141e10 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 11 May 2016 15:16:13 +0200 Subject: builtin/apply: avoid parameter shadowing 'p_value' global MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's just rename the global 'state_p_value' as it will become 'state->p_value' in a following patch. This also avoid errors when compiling with -Wshadow and makes it safer to later move global variables into a "state" struct. Helped-by: Nguyễn Thái Ngọc Duy Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index fe5aebdb0f..e133b381d3 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -35,7 +35,7 @@ static int prefix_length = -1; static int newfd = -1; static int unidiff_zero; -static int p_value = 1; +static int state_p_value = 1; static int p_value_known; static int check_index; static int update_index; @@ -872,24 +872,24 @@ static void parse_traditional_patch(const char *first, const char *second, struc q = guess_p_value(second); if (p < 0) p = q; if (0 <= p && p == q) { - p_value = p; + state_p_value = p; p_value_known = 1; } } if (is_dev_null(first)) { patch->is_new = 1; patch->is_delete = 0; - name = find_name_traditional(second, NULL, p_value); + name = find_name_traditional(second, NULL, state_p_value); patch->new_name = name; } else if (is_dev_null(second)) { patch->is_new = 0; patch->is_delete = 1; - name = find_name_traditional(first, NULL, p_value); + name = find_name_traditional(first, NULL, state_p_value); patch->old_name = name; } else { char *first_name; - first_name = find_name_traditional(first, NULL, p_value); - name = find_name_traditional(second, first_name, p_value); + first_name = find_name_traditional(first, NULL, state_p_value); + name = find_name_traditional(second, first_name, state_p_value); free(first_name); if (has_epoch_timestamp(first)) { patch->is_new = 1; @@ -928,7 +928,7 @@ static int gitdiff_hdrend(const char *line, struct patch *patch) static void gitdiff_verify_name(const char *line, int isnull, char **name, int side) { if (!*name && !isnull) { - *name = find_name(line, NULL, p_value, TERM_TAB); + *name = find_name(line, NULL, state_p_value, TERM_TAB); return; } @@ -938,7 +938,7 @@ static void gitdiff_verify_name(const char *line, int isnull, char **name, int s if (isnull) die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), *name, linenr); - another = find_name(line, NULL, p_value, TERM_TAB); + another = find_name(line, NULL, state_p_value, TERM_TAB); if (!another || memcmp(another, *name, len + 1)) die((side == DIFF_NEW_NAME) ? _("git apply: bad git-diff - inconsistent new filename on line %d") : @@ -997,7 +997,7 @@ static int gitdiff_copysrc(const char *line, struct patch *patch) { patch->is_copy = 1; free(patch->old_name); - patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0); + patch->old_name = find_name(line, NULL, state_p_value ? state_p_value - 1 : 0, 0); return 0; } @@ -1005,7 +1005,7 @@ static int gitdiff_copydst(const char *line, struct patch *patch) { patch->is_copy = 1; free(patch->new_name); - patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0); + patch->new_name = find_name(line, NULL, state_p_value ? state_p_value - 1 : 0, 0); return 0; } @@ -1013,7 +1013,7 @@ static int gitdiff_renamesrc(const char *line, struct patch *patch) { patch->is_rename = 1; free(patch->old_name); - patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0); + patch->old_name = find_name(line, NULL, state_p_value ? state_p_value - 1 : 0, 0); return 0; } @@ -1021,7 +1021,7 @@ static int gitdiff_renamedst(const char *line, struct patch *patch) { patch->is_rename = 1; free(patch->new_name); - patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0); + patch->new_name = find_name(line, NULL, state_p_value ? state_p_value - 1 : 0, 0); return 0; } @@ -1092,10 +1092,10 @@ static const char *skip_tree_prefix(const char *line, int llen) int nslash; int i; - if (!p_value) + if (!state_p_value) return (llen && line[0] == '/') ? NULL : line; - nslash = p_value; + nslash = state_p_value; for (i = 0; i < llen; i++) { int ch = line[i]; if (ch == '/' && --nslash <= 0) @@ -1481,8 +1481,8 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc "%d leading pathname component (line %d)", "git diff header lacks filename information when removing " "%d leading pathname components (line %d)", - p_value), - p_value, linenr); + state_p_value), + state_p_value, linenr); patch->old_name = xstrdup(patch->def_name); patch->new_name = xstrdup(patch->def_name); } @@ -4461,7 +4461,7 @@ static int option_parse_include(const struct option *opt, static int option_parse_p(const struct option *opt, const char *arg, int unset) { - p_value = atoi(arg); + state_p_value = atoi(arg); p_value_known = 1; return 0; } -- cgit v1.2.1 From eb8fdbff3c26639912e73a01cfa2a86ce787d4b2 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 11 May 2016 15:16:14 +0200 Subject: builtin/apply: avoid parameter shadowing 'linenr' global Let's just rename the global 'state_linenr' as it will become 'state->linenr' in a following patch. This also avoid errors when compiling with -Wshadow and makes it safer to later move global variables into a "state" struct. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index e133b381d3..705a9c8bd1 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -144,7 +144,7 @@ static int max_change, max_len; * file (and how) we're patching right now.. The "is_xxxx" * things are flags, where -1 means "don't know yet". */ -static int linenr = 1; +static int state_linenr = 1; /* * This represents one "hunk" from a patch, starting with @@ -905,7 +905,7 @@ static void parse_traditional_patch(const char *first, const char *second, struc } } if (!name) - die(_("unable to find filename in patch at line %d"), linenr); + die(_("unable to find filename in patch at line %d"), state_linenr); } static int gitdiff_hdrend(const char *line, struct patch *patch) @@ -937,17 +937,17 @@ static void gitdiff_verify_name(const char *line, int isnull, char **name, int s char *another; if (isnull) die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), - *name, linenr); + *name, state_linenr); another = find_name(line, NULL, state_p_value, TERM_TAB); if (!another || memcmp(another, *name, len + 1)) die((side == DIFF_NEW_NAME) ? _("git apply: bad git-diff - inconsistent new filename on line %d") : - _("git apply: bad git-diff - inconsistent old filename on line %d"), linenr); + _("git apply: bad git-diff - inconsistent old filename on line %d"), state_linenr); free(another); } else { /* expect "/dev/null" */ if (memcmp("/dev/null", line, 9) || line[9] != '\n') - die(_("git apply: bad git-diff - expected /dev/null on line %d"), linenr); + die(_("git apply: bad git-diff - expected /dev/null on line %d"), state_linenr); } } @@ -1272,8 +1272,8 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct line += len; size -= len; - linenr++; - for (offset = len ; size > 0 ; offset += len, size -= len, line += len, linenr++) { + state_linenr++; + for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state_linenr++) { static const struct opentry { const char *str; int (*fn)(const char *, struct patch *); @@ -1440,7 +1440,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc patch->is_new = patch->is_delete = -1; patch->old_mode = patch->new_mode = 0; patch->old_name = patch->new_name = NULL; - for (offset = 0; size > 0; offset += len, size -= len, line += len, linenr++) { + for (offset = 0; size > 0; offset += len, size -= len, line += len, state_linenr++) { unsigned long nextlen; len = linelen(line, size); @@ -1461,7 +1461,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc if (parse_fragment_header(line, len, &dummy) < 0) continue; die(_("patch fragment without header at line %d: %.*s"), - linenr, (int)len-1, line); + state_linenr, (int)len-1, line); } if (size < len + 6) @@ -1482,13 +1482,13 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc "git diff header lacks filename information when removing " "%d leading pathname components (line %d)", state_p_value), - state_p_value, linenr); + state_p_value, state_linenr); patch->old_name = xstrdup(patch->def_name); patch->new_name = xstrdup(patch->def_name); } if (!patch->is_delete && !patch->new_name) die("git diff header lacks filename information " - "(line %d)", linenr); + "(line %d)", state_linenr); patch->is_toplevel_relative = 1; *hdrsize = git_hdr_len; return offset; @@ -1510,7 +1510,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc /* Ok, we'll consider it a patch */ parse_traditional_patch(line, line+len, patch); *hdrsize = len + nextlen; - linenr += 2; + state_linenr += 2; return offset; } return -1; @@ -1538,7 +1538,7 @@ static void check_whitespace(const char *line, int len, unsigned ws_rule) { unsigned result = ws_check(line + 1, len - 1, ws_rule); - record_ws_error(result, line + 1, len - 2, linenr); + record_ws_error(result, line + 1, len - 2, state_linenr); } /* @@ -1568,11 +1568,11 @@ static int parse_fragment(const char *line, unsigned long size, /* Parse the thing.. */ line += len; size -= len; - linenr++; + state_linenr++; added = deleted = 0; for (offset = len; 0 < size; - offset += len, size -= len, line += len, linenr++) { + offset += len, size -= len, line += len, state_linenr++) { if (!oldlines && !newlines) break; len = linelen(line, size); @@ -1668,10 +1668,10 @@ static int parse_single_patch(const char *line, unsigned long size, struct patch int len; fragment = xcalloc(1, sizeof(*fragment)); - fragment->linenr = linenr; + fragment->linenr = state_linenr; len = parse_fragment(line, size, patch, fragment); if (len <= 0) - die(_("corrupt patch at line %d"), linenr); + die(_("corrupt patch at line %d"), state_linenr); fragment->patch = line; fragment->size = len; oldlines += fragment->oldlines; @@ -1799,13 +1799,13 @@ static struct fragment *parse_binary_hunk(char **buf_p, else return NULL; - linenr++; + state_linenr++; buffer += llen; while (1) { int byte_length, max_byte_length, newsize; llen = linelen(buffer, size); used += llen; - linenr++; + state_linenr++; if (llen == 1) { /* consume the blank line */ buffer++; @@ -1859,7 +1859,7 @@ static struct fragment *parse_binary_hunk(char **buf_p, free(data); *status_p = -1; error(_("corrupt binary patch at line %d: %.*s"), - linenr-1, llen-1, buffer); + state_linenr-1, llen-1, buffer); return NULL; } @@ -1892,7 +1892,7 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch) forward = parse_binary_hunk(&buffer, &size, &status, &used); if (!forward && !status) /* there has to be one hunk (forward hunk) */ - return error(_("unrecognized binary patch at line %d"), linenr-1); + return error(_("unrecognized binary patch at line %d"), state_linenr-1); if (status) /* otherwise we already gave an error message */ return status; @@ -2010,7 +2010,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) if (llen == sizeof(git_binary) - 1 && !memcmp(git_binary, buffer + hd, llen)) { int used; - linenr++; + state_linenr++; used = parse_binary(buffer + hd + llen, size - hd - llen, patch); if (used < 0) @@ -2031,7 +2031,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) int len = strlen(binhdr[i]); if (len < size - hd && !memcmp(binhdr[i], buffer + hd, len)) { - linenr++; + state_linenr++; patch->is_binary = 1; patchsize = llen; break; @@ -2045,7 +2045,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) */ if ((apply || check) && (!patch->is_binary && !metadata_changes(patch))) - die(_("patch with only garbage at line %d"), linenr); + die(_("patch with only garbage at line %d"), state_linenr); } return offset + hdrsize + patchsize; -- cgit v1.2.1 From bb0ba9974314da72bba9a6200fa3bd61c4d9eab4 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 11 May 2016 15:16:15 +0200 Subject: builtin/apply: avoid local variable shadowing 'len' parameter This is just a cleanup to avoid errors when compiling with -Wshadow and to make it safer to later move global variables into a "state" struct. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 705a9c8bd1..bb8bf7f0e3 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -2194,17 +2194,17 @@ static void update_pre_post_images(struct image *preimage, fixed = preimage->buf; for (i = reduced = ctx = 0; i < postimage->nr; i++) { - size_t len = postimage->line[i].len; + size_t l_len = postimage->line[i].len; if (!(postimage->line[i].flag & LINE_COMMON)) { /* an added line -- no counterparts in preimage */ - memmove(new, old, len); - old += len; - new += len; + memmove(new, old, l_len); + old += l_len; + new += l_len; continue; } /* a common context -- skip it in the original postimage */ - old += len; + old += l_len; /* and find the corresponding one in the fixed preimage */ while (ctx < preimage->nr && @@ -2223,11 +2223,11 @@ static void update_pre_post_images(struct image *preimage, } /* and copy it in, while fixing the line length */ - len = preimage->line[ctx].len; - memcpy(new, fixed, len); - new += len; - fixed += len; - postimage->line[i].len = len; + l_len = preimage->line[ctx].len; + memcpy(new, fixed, l_len); + new += l_len; + fixed += l_len; + postimage->line[i].len = l_len; ctx++; } -- cgit v1.2.1 From 7a3eb9e2224d883df229fe4adce51e762165573a Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 11 May 2016 15:16:16 +0200 Subject: builtin/apply: extract line_by_line_fuzzy_match() from match_fragment() The match_fragment() function is very big and contains a big special case algorithm that does line by line fuzzy matching. So let's extract this algorithm in a separate line_by_line_fuzzy_match() function. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 126 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 71 insertions(+), 55 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index bb8bf7f0e3..7bab466fe2 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -2242,6 +2242,74 @@ static void update_pre_post_images(struct image *preimage, postimage->nr -= reduced; } +static int line_by_line_fuzzy_match(struct image *img, + struct image *preimage, + struct image *postimage, + unsigned long try, + int try_lno, + int preimage_limit) +{ + int i; + size_t imgoff = 0; + size_t preoff = 0; + size_t postlen = postimage->len; + size_t extra_chars; + char *buf; + char *preimage_eof; + char *preimage_end; + struct strbuf fixed; + char *fixed_buf; + size_t fixed_len; + + for (i = 0; i < preimage_limit; i++) { + size_t prelen = preimage->line[i].len; + size_t imglen = img->line[try_lno+i].len; + + if (!fuzzy_matchlines(img->buf + try + imgoff, imglen, + preimage->buf + preoff, prelen)) + return 0; + if (preimage->line[i].flag & LINE_COMMON) + postlen += imglen - prelen; + imgoff += imglen; + preoff += prelen; + } + + /* + * Ok, the preimage matches with whitespace fuzz. + * + * imgoff now holds the true length of the target that + * matches the preimage before the end of the file. + * + * Count the number of characters in the preimage that fall + * beyond the end of the file and make sure that all of them + * are whitespace characters. (This can only happen if + * we are removing blank lines at the end of the file.) + */ + buf = preimage_eof = preimage->buf + preoff; + for ( ; i < preimage->nr; i++) + preoff += preimage->line[i].len; + preimage_end = preimage->buf + preoff; + for ( ; buf < preimage_end; buf++) + if (!isspace(*buf)) + return 0; + + /* + * Update the preimage and the common postimage context + * lines to use the same whitespace as the target. + * If whitespace is missing in the target (i.e. + * if the preimage extends beyond the end of the file), + * use the whitespace from the preimage. + */ + extra_chars = preimage_end - preimage_eof; + strbuf_init(&fixed, imgoff + extra_chars); + strbuf_add(&fixed, img->buf + try, imgoff); + strbuf_add(&fixed, preimage_eof, extra_chars); + fixed_buf = strbuf_detach(&fixed, &fixed_len); + update_pre_post_images(preimage, postimage, + fixed_buf, fixed_len, postlen); + return 1; +} + static int match_fragment(struct image *img, struct image *preimage, struct image *postimage, @@ -2331,61 +2399,9 @@ static int match_fragment(struct image *img, * fuzzy matching. We collect all the line length information because * we need it to adjust whitespace if we match. */ - if (ws_ignore_action == ignore_ws_change) { - size_t imgoff = 0; - size_t preoff = 0; - size_t postlen = postimage->len; - size_t extra_chars; - char *preimage_eof; - char *preimage_end; - for (i = 0; i < preimage_limit; i++) { - size_t prelen = preimage->line[i].len; - size_t imglen = img->line[try_lno+i].len; - - if (!fuzzy_matchlines(img->buf + try + imgoff, imglen, - preimage->buf + preoff, prelen)) - return 0; - if (preimage->line[i].flag & LINE_COMMON) - postlen += imglen - prelen; - imgoff += imglen; - preoff += prelen; - } - - /* - * Ok, the preimage matches with whitespace fuzz. - * - * imgoff now holds the true length of the target that - * matches the preimage before the end of the file. - * - * Count the number of characters in the preimage that fall - * beyond the end of the file and make sure that all of them - * are whitespace characters. (This can only happen if - * we are removing blank lines at the end of the file.) - */ - buf = preimage_eof = preimage->buf + preoff; - for ( ; i < preimage->nr; i++) - preoff += preimage->line[i].len; - preimage_end = preimage->buf + preoff; - for ( ; buf < preimage_end; buf++) - if (!isspace(*buf)) - return 0; - - /* - * Update the preimage and the common postimage context - * lines to use the same whitespace as the target. - * If whitespace is missing in the target (i.e. - * if the preimage extends beyond the end of the file), - * use the whitespace from the preimage. - */ - extra_chars = preimage_end - preimage_eof; - strbuf_init(&fixed, imgoff + extra_chars); - strbuf_add(&fixed, img->buf + try, imgoff); - strbuf_add(&fixed, preimage_eof, extra_chars); - fixed_buf = strbuf_detach(&fixed, &fixed_len); - update_pre_post_images(preimage, postimage, - fixed_buf, fixed_len, postlen); - return 1; - } + if (ws_ignore_action == ignore_ws_change) + return line_by_line_fuzzy_match(img, preimage, postimage, + try, try_lno, preimage_limit); if (ws_error_action != correct_ws_error) return 0; -- cgit v1.2.1 From d1b27ced9a38ed03e99422b6cb24ff95c7437663 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 11 May 2016 15:16:17 +0200 Subject: builtin/apply: move 'options' variable into cmd_apply() The 'options' variable doesn't need to be static and global to the file. It can be local to cmd_apply(), so let's move it there. This will make it easier to libify the apply functionality. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/apply.c b/builtin/apply.c index 7bab466fe2..5a1d65a695 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -79,7 +79,6 @@ static enum ws_ignore { static const char *patch_input_file; static struct strbuf root = STRBUF_INIT; static int read_stdin = 1; -static int options; static void parse_whitespace_option(const char *option) { @@ -4517,6 +4516,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) int errs = 0; int is_not_gitdir = !startup_info->have_repository; int force_apply = 0; + int options = 0; const char *whitespace_option = NULL; -- cgit v1.2.1 From dcde8b3dcd8d4411a2311fa0a93ca4ca3b26d61f Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 11 May 2016 15:16:18 +0200 Subject: builtin/apply: move 'read_stdin' global into cmd_apply() The 'read_stdin' variable doesn't need to be static and global to the file. It can be local to cmd_apply(), so let's move it there. This will make it easier to libify the apply functionality. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/apply.c b/builtin/apply.c index 5a1d65a695..c911e4ec82 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -78,7 +78,6 @@ static enum ws_ignore { static const char *patch_input_file; static struct strbuf root = STRBUF_INIT; -static int read_stdin = 1; static void parse_whitespace_option(const char *option) { @@ -4517,6 +4516,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) int is_not_gitdir = !startup_info->have_repository; int force_apply = 0; int options = 0; + int read_stdin = 1; const char *whitespace_option = NULL; -- cgit v1.2.1 From 2fc0f1849bd0d6100719bb53f89f15fbd734157a Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 11 May 2016 15:16:19 +0200 Subject: builtin/apply: introduce 'struct apply_state' to start libifying Currently commands that want to use the apply functionality have to launch a "git apply" process which can be bad for performance. Let's start libifying the apply functionality and to do that we first need to get rid of the global variables in "builtin/apply.c". This patch introduces "struct apply_state" into which all the previously global variables will be moved. A new parameter called "state" that is a pointer to the "apply_state" structure will come at the beginning of the helper functions that need it and will be passed around the call chain. To start let's move the "prefix" and "prefix_length" global variables into "struct apply_state". Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 94 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index c911e4ec82..ae068e7392 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -21,6 +21,11 @@ #include "ll-merge.h" #include "rerere.h" +struct apply_state { + const char *prefix; + int prefix_length; +}; + /* * --check turns on checking that the working tree matches the * files that are being modified, but doesn't apply the patch @@ -30,8 +35,6 @@ * --index updates the cache as well. * --cached updates only the cache without ever touching the working tree. */ -static const char *prefix; -static int prefix_length = -1; static int newfd = -1; static int unidiff_zero; @@ -748,7 +751,7 @@ static int count_slashes(const char *cp) * Given the string after "--- " or "+++ ", guess the appropriate * p_value for the given patch. */ -static int guess_p_value(const char *nameline) +static int guess_p_value(struct apply_state *state, const char *nameline) { char *name, *cp; int val = -1; @@ -761,17 +764,17 @@ static int guess_p_value(const char *nameline) cp = strchr(name, '/'); if (!cp) val = 0; - else if (prefix) { + else if (state->prefix) { /* * Does it begin with "a/$our-prefix" and such? Then this is * very likely to apply to our directory. */ - if (!strncmp(name, prefix, prefix_length)) - val = count_slashes(prefix); + if (!strncmp(name, state->prefix, state->prefix_length)) + val = count_slashes(state->prefix); else { cp++; - if (!strncmp(cp, prefix, prefix_length)) - val = count_slashes(prefix) + 1; + if (!strncmp(cp, state->prefix, state->prefix_length)) + val = count_slashes(state->prefix) + 1; } } free(name); @@ -858,7 +861,10 @@ static int has_epoch_timestamp(const char *nameline) * files, we can happily check the index for a match, but for creating a * new file we should try to match whatever "patch" does. I have no idea. */ -static void parse_traditional_patch(const char *first, const char *second, struct patch *patch) +static void parse_traditional_patch(struct apply_state *state, + const char *first, + const char *second, + struct patch *patch) { char *name; @@ -866,8 +872,8 @@ static void parse_traditional_patch(const char *first, const char *second, struc second += 4; /* skip "+++ " */ if (!p_value_known) { int p, q; - p = guess_p_value(first); - q = guess_p_value(second); + p = guess_p_value(state, first); + q = guess_p_value(state, second); if (p < 0) p = q; if (0 <= p && p == q) { state_p_value = p; @@ -1429,7 +1435,11 @@ static int parse_fragment_header(const char *line, int len, struct fragment *fra return offset; } -static int find_header(const char *line, unsigned long size, int *hdrsize, struct patch *patch) +static int find_header(struct apply_state *state, + const char *line, + unsigned long size, + int *hdrsize, + struct patch *patch) { unsigned long offset, len; @@ -1506,7 +1516,7 @@ static int find_header(const char *line, unsigned long size, int *hdrsize, struc continue; /* Ok, we'll consider it a patch */ - parse_traditional_patch(line, line+len, patch); + parse_traditional_patch(state, line, line+len, patch); *hdrsize = len + nextlen; state_linenr += 2; return offset; @@ -1913,21 +1923,21 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch) return used; } -static void prefix_one(char **name) +static void prefix_one(struct apply_state *state, char **name) { char *old_name = *name; if (!old_name) return; - *name = xstrdup(prefix_filename(prefix, prefix_length, *name)); + *name = xstrdup(prefix_filename(state->prefix, state->prefix_length, *name)); free(old_name); } -static void prefix_patch(struct patch *p) +static void prefix_patch(struct apply_state *state, struct patch *p) { - if (!prefix || p->is_toplevel_relative) + if (!state->prefix || p->is_toplevel_relative) return; - prefix_one(&p->new_name); - prefix_one(&p->old_name); + prefix_one(state, &p->new_name); + prefix_one(state, &p->old_name); } /* @@ -1944,16 +1954,16 @@ static void add_name_limit(const char *name, int exclude) it->util = exclude ? NULL : (void *) 1; } -static int use_patch(struct patch *p) +static int use_patch(struct apply_state *state, struct patch *p) { const char *pathname = p->new_name ? p->new_name : p->old_name; int i; /* Paths outside are not touched regardless of "--include" */ - if (0 < prefix_length) { + if (0 < state->prefix_length) { int pathlen = strlen(pathname); - if (pathlen <= prefix_length || - memcmp(prefix, pathname, prefix_length)) + if (pathlen <= state->prefix_length || + memcmp(state->prefix, pathname, state->prefix_length)) return 0; } @@ -1980,17 +1990,17 @@ static int use_patch(struct patch *p) * Return the number of bytes consumed, so that the caller can call us * again for the next patch. */ -static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) +static int parse_chunk(struct apply_state *state, char *buffer, unsigned long size, struct patch *patch) { int hdrsize, patchsize; - int offset = find_header(buffer, size, &hdrsize, patch); + int offset = find_header(state, buffer, size, &hdrsize, patch); if (offset < 0) return offset; - prefix_patch(patch); + prefix_patch(state, patch); - if (!use_patch(patch)) + if (!use_patch(state, patch)) patch->ws_rule = 0; else patch->ws_rule = whitespace_rule(patch->new_name @@ -4367,7 +4377,10 @@ static struct lock_file lock_file; #define INACCURATE_EOF (1<<0) #define RECOUNT (1<<1) -static int apply_patch(int fd, const char *filename, int options) +static int apply_patch(struct apply_state *state, + int fd, + const char *filename, + int options) { size_t offset; struct strbuf buf = STRBUF_INIT; /* owns the patch text */ @@ -4384,14 +4397,14 @@ static int apply_patch(int fd, const char *filename, int options) patch = xcalloc(1, sizeof(*patch)); patch->inaccurate_eof = !!(options & INACCURATE_EOF); patch->recount = !!(options & RECOUNT); - nr = parse_chunk(buf.buf + offset, buf.len - offset, patch); + nr = parse_chunk(state, buf.buf + offset, buf.len - offset, patch); if (nr < 0) { free_patch(patch); break; } if (apply_in_reverse) reverse_patches(patch); - if (use_patch(patch)) { + if (use_patch(state, patch)) { patch_stats(patch); *listp = patch; listp = &patch->next; @@ -4517,6 +4530,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) int force_apply = 0; int options = 0; int read_stdin = 1; + struct apply_state state; const char *whitespace_option = NULL; @@ -4589,15 +4603,17 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) OPT_END() }; - prefix = prefix_; - prefix_length = prefix ? strlen(prefix) : 0; + memset(&state, 0, sizeof(state)); + state.prefix = prefix_; + state.prefix_length = state.prefix ? strlen(state.prefix) : 0; + git_apply_config(); if (apply_default_whitespace) parse_whitespace_option(apply_default_whitespace); if (apply_default_ignorewhitespace) parse_ignorewhitespace_option(apply_default_ignorewhitespace); - argc = parse_options(argc, argv, prefix, builtin_apply_options, + argc = parse_options(argc, argv, state.prefix, builtin_apply_options, apply_usage, 0); if (apply_with_reject && threeway) @@ -4628,23 +4644,25 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) int fd; if (!strcmp(arg, "-")) { - errs |= apply_patch(0, "", options); + errs |= apply_patch(&state, 0, "", options); read_stdin = 0; continue; - } else if (0 < prefix_length) - arg = prefix_filename(prefix, prefix_length, arg); + } else if (0 < state.prefix_length) + arg = prefix_filename(state.prefix, + state.prefix_length, + arg); fd = open(arg, O_RDONLY); if (fd < 0) die_errno(_("can't open patch '%s'"), arg); read_stdin = 0; set_default_whitespace_mode(whitespace_option); - errs |= apply_patch(fd, arg, options); + errs |= apply_patch(&state, fd, arg, options); close(fd); } set_default_whitespace_mode(whitespace_option); if (read_stdin) - errs |= apply_patch(0, "", options); + errs |= apply_patch(&state, 0, "", options); if (whitespace_error) { if (squelch_whitespace_errors && squelch_whitespace_errors < whitespace_error) { -- cgit v1.2.1 From c57e501c51d6b76ce30658b94ee4a5dc6ac27f3e Mon Sep 17 00:00:00 2001 From: Alexander Hirsch <1zeeky@gmail.com> Date: Fri, 20 May 2016 23:00:54 +0200 Subject: pull: warn on --verify-signatures with --rebase git-pull silently ignores the --verify-signatures option when running --rebase, potentially leaving users in the belief that the rebase operation would check for valid GPG signatures. Implementing --verify-signatures for git-rebase was talked about, but doubts for a valid workflow rose up. Since you usually merge other's branches into your branch you might have an interest that their side has a valid GPG signature. Rebasing, on the other hand, is to rebuild your branch on top of other's work, in order to push the result back, and it is too late to reject their work even if you find their commits lack acceptable signature. Let's warn users that the --verify-signatures option is ignored during "pull --rebase"; users do not wonder what would happen if their commits lack acceptable signature that way. Signed-off-by: Alexander Hirsch <1zeeky@gmail.com> Signed-off-by: Junio C Hamano --- builtin/pull.c | 3 +++ t/t5520-pull.sh | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/builtin/pull.c b/builtin/pull.c index 1d7333c8a1..897a7f4e4e 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -815,6 +815,9 @@ static int run_rebase(const unsigned char *curr_head, argv_array_push(&args, "--no-autostash"); else if (opt_autostash == 1) argv_array_push(&args, "--autostash"); + if (opt_verify_signatures && + !strcmp(opt_verify_signatures, "--verify-signatures")) + warning(_("ignoring --verify-signatures for rebase")); argv_array_push(&args, "--onto"); argv_array_push(&args, sha1_to_hex(merge_head)); diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 739c089d50..3159956fde 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -341,6 +341,22 @@ test_expect_success 'branch.to-rebase.rebase should override pull.rebase' ' test new = "$(git show HEAD:file2)" ' +test_expect_success "pull --rebase warns on --verify-signatures" ' + git reset --hard before-rebase && + git pull --rebase --verify-signatures . copy 2>err && + test "$(git rev-parse HEAD^)" = "$(git rev-parse copy)" && + test new = "$(git show HEAD:file2)" && + test_i18ngrep "ignoring --verify-signatures for rebase" err +' + +test_expect_success "pull --rebase does not warn on --no-verify-signatures" ' + git reset --hard before-rebase && + git pull --rebase --no-verify-signatures . copy 2>err && + test "$(git rev-parse HEAD^)" = "$(git rev-parse copy)" && + test new = "$(git show HEAD:file2)" && + test_i18ngrep ! "verify-signatures" err +' + # add a feature branch, keep-merge, that is merged into master, so the # test can try preserving the merge commit (or not) with various # --rebase flags/pull.rebase settings. -- cgit v1.2.1 From b462c024022e3a41821f24bca9e43f16973693db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 22 May 2016 16:33:51 +0700 Subject: completion: support git-worktree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds bare-bone completion support for git-worktree. More advanced completion (e.g. ref completion in git-worktree-add) can be added later. --force completion in "worktree add" is left out because that option should be handled with care. Signed-off-by: Nguyễn Thái Ngọc Duy Reviewed-by: Eric Sunshine Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 34024754d9..951a186df0 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2595,6 +2595,29 @@ _git_whatchanged () _git_log } +_git_worktree () +{ + local subcommands="add list prune" + local subcommand="$(__git_find_on_cmdline "$subcommands")" + if [ -z "$subcommand" ]; then + __gitcomp "$subcommands" + else + case "$subcommand,$cur" in + add,--*) + __gitcomp "--detach" + ;; + list,--*) + __gitcomp "--porcelain" + ;; + prune,--*) + __gitcomp "--dry-run --expire --verbose" + ;; + *) + ;; + esac + fi +} + __git_main () { local i c=1 command __git_dir -- cgit v1.2.1 From 360af2dadaae70f29de2f21d4eb8ac38aefcc263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 22 May 2016 16:33:52 +0700 Subject: worktree.c: rewrite mark_current_worktree() to avoid strbuf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit strbuf is a bit overkill for this function. What we need is to call absolute_path() twice and make sure the second call does not destroy the result of the first. One buffer allocation is enough. Signed-off-by: Nguyễn Thái Ngọc Duy Reviewed-by: Eric Sunshine Signed-off-by: Junio C Hamano --- worktree.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/worktree.c b/worktree.c index 4817d60055..6a11611e60 100644 --- a/worktree.c +++ b/worktree.c @@ -153,21 +153,19 @@ done: static void mark_current_worktree(struct worktree **worktrees) { - struct strbuf git_dir = STRBUF_INIT; - struct strbuf path = STRBUF_INIT; + char *git_dir = xstrdup(absolute_path(get_git_dir())); int i; - strbuf_addstr(&git_dir, absolute_path(get_git_dir())); for (i = 0; worktrees[i]; i++) { struct worktree *wt = worktrees[i]; - strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt))); - wt->is_current = !fspathcmp(git_dir.buf, path.buf); - strbuf_reset(&path); - if (wt->is_current) + const char *wt_git_dir = get_worktree_git_dir(wt); + + if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) { + wt->is_current = 1; break; + } } - strbuf_release(&git_dir); - strbuf_release(&path); + free(git_dir); } struct worktree **get_worktrees(void) -- cgit v1.2.1 From 7b722d906bd5b32f3c7a63fb77835b38c1e04e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 22 May 2016 16:33:53 +0700 Subject: git-worktree.txt: keep subcommand listing in alphabetical order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is probably not the best order. But it makes it no-brainer to know where to insert new commands. At some point we might want to reorder at least the synopsis part again, grouping commonly use subcommands together. Signed-off-by: Nguyễn Thái Ngọc Duy Reviewed-by: Eric Sunshine Signed-off-by: Junio C Hamano --- Documentation/git-worktree.txt | 10 +++++----- builtin/worktree.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt index c62234538b..27feff6dba 100644 --- a/Documentation/git-worktree.txt +++ b/Documentation/git-worktree.txt @@ -10,8 +10,8 @@ SYNOPSIS -------- [verse] 'git worktree add' [-f] [--detach] [--checkout] [-b ] [] -'git worktree prune' [-n] [-v] [--expire ] 'git worktree list' [--porcelain] +'git worktree prune' [-n] [-v] [--expire ] DESCRIPTION ----------- @@ -54,10 +54,6 @@ If `` is omitted and neither `-b` nor `-B` nor `--detached` used, then, as a convenience, a new branch based at HEAD is created automatically, as if `-b $(basename )` was specified. -prune:: - -Prune working tree information in $GIT_DIR/worktrees. - list:: List details of each worktree. The main worktree is listed first, followed by @@ -65,6 +61,10 @@ each of the linked worktrees. The output details include if the worktree is bare, the revision currently checked out, and the branch currently checked out (or 'detached HEAD' if none). +prune:: + +Prune working tree information in $GIT_DIR/worktrees. + OPTIONS ------- diff --git a/builtin/worktree.c b/builtin/worktree.c index 12c0af723e..bf80111fd8 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -13,8 +13,8 @@ static const char * const worktree_usage[] = { N_("git worktree add [] []"), - N_("git worktree prune []"), N_("git worktree list []"), + N_("git worktree prune []"), NULL }; -- cgit v1.2.1 From afb9e30b2cadfa010975b29e0ff7459d4b7ae953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 22 May 2016 16:33:54 +0700 Subject: worktree.c: use is_dot_or_dotdot() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Reviewed-by: Eric Sunshine Signed-off-by: Junio C Hamano --- builtin/worktree.c | 2 +- worktree.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/worktree.c b/builtin/worktree.c index bf80111fd8..aaee0e2fe8 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -95,7 +95,7 @@ static void prune_worktrees(void) if (!dir) return; while ((d = readdir(dir)) != NULL) { - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + if (is_dot_or_dotdot(d->d_name)) continue; strbuf_reset(&reason); if (!prune_worktree(d->d_name, &reason)) diff --git a/worktree.c b/worktree.c index 6a11611e60..f4a4f38092 100644 --- a/worktree.c +++ b/worktree.c @@ -187,7 +187,7 @@ struct worktree **get_worktrees(void) if (dir) { while ((d = readdir(dir)) != NULL) { struct worktree *linked = NULL; - if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + if (is_dot_or_dotdot(d->d_name)) continue; if ((linked = get_linked_worktree(d->d_name))) { -- cgit v1.2.1 From ef23c347cf363118e236ae72fe6372443fae1ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 22 May 2016 16:33:55 +0700 Subject: worktree: avoid 0{40}, too many zeroes, hard to read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Reviewed-by: Eric Sunshine Signed-off-by: Junio C Hamano --- builtin/worktree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/worktree.c b/builtin/worktree.c index aaee0e2fe8..b53f8024fc 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -262,7 +262,7 @@ static int add_worktree(const char *path, const char *refname, */ strbuf_reset(&sb); strbuf_addf(&sb, "%s/HEAD", sb_repo.buf); - write_file(sb.buf, "0000000000000000000000000000000000000000"); + write_file(sb.buf, sha1_to_hex(null_sha1)); strbuf_reset(&sb); strbuf_addf(&sb, "%s/commondir", sb_repo.buf); write_file(sb.buf, "../.."); -- cgit v1.2.1 From 0409e0b6dc169b07c05d02e8b62389b1cc975a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 22 May 2016 16:33:56 +0700 Subject: worktree: simplify prefixing paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also makes slash conversion always happen on Windows (a side effect of prefix_filename). Which is a good thing. Signed-off-by: Nguyễn Thái Ngọc Duy Reviewed-by: Eric Sunshine Signed-off-by: Junio C Hamano --- builtin/worktree.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/builtin/worktree.c b/builtin/worktree.c index b53f8024fc..f9dac376f7 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -337,7 +337,7 @@ static int add(int ac, const char **av, const char *prefix) if (ac < 1 || ac > 2) usage_with_options(worktree_usage, options); - path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0]; + path = prefix_filename(prefix, strlen(prefix), av[0]); branch = ac < 2 ? "HEAD" : av[1]; opts.force_new_branch = !!new_branch_force; @@ -467,6 +467,8 @@ int cmd_worktree(int ac, const char **av, const char *prefix) if (ac < 2) usage_with_options(worktree_usage, options); + if (!prefix) + prefix = ""; if (!strcmp(av[1], "add")) return add(ac - 1, av + 1, prefix); if (!strcmp(av[1], "prune")) -- cgit v1.2.1 From c72ee44bf4bc7549ee8710037ae8adfae6d8efc2 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 18 May 2016 18:39:02 -0400 Subject: git_config_with_options: drop "found" counting Prior to 1f2baa7 (config: treat non-existent config files as empty, 2010-10-21), we returned an error if any config files were missing. That commit made this a non-error, but returned the number of sources found, in case any caller wanted to distinguish this case. In the past 5+ years, no caller has; the only two places which bother to check the return value care only about the error case. Let's drop this code, which complicates the function. Similarly, let's drop the "found anything" return from git_config_from_parameters, which was present only to support this (and similarly has never had other callers care for the past 5+ years). Note that we do need to update a comment in one of the callers, even though the code immediately below it doesn't care about this case. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- config.c | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/config.c b/config.c index f51c56bf92..771d0ddbec 100644 --- a/config.c +++ b/config.c @@ -230,7 +230,7 @@ int git_config_from_parameters(config_fn_t fn, void *data) free(argv); free(envw); - return nr > 0; + return 0; } static int get_next_char(void) @@ -1197,47 +1197,31 @@ int git_config_system(void) static int do_git_config_sequence(config_fn_t fn, void *data) { - int ret = 0, found = 0; + int ret = 0; char *xdg_config = xdg_config_home("config"); char *user_config = expand_user_path("~/.gitconfig"); char *repo_config = git_pathdup("config"); - if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) { + if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) ret += git_config_from_file(fn, git_etc_gitconfig(), data); - found += 1; - } - if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) { + if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) ret += git_config_from_file(fn, xdg_config, data); - found += 1; - } - if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) { + if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) ret += git_config_from_file(fn, user_config, data); - found += 1; - } - if (repo_config && !access_or_die(repo_config, R_OK, 0)) { + if (repo_config && !access_or_die(repo_config, R_OK, 0)) ret += git_config_from_file(fn, repo_config, data); - found += 1; - } - switch (git_config_from_parameters(fn, data)) { - case -1: /* error */ + if (git_config_from_parameters(fn, data) < 0) die(_("unable to parse command-line config")); - break; - case 0: /* found nothing */ - break; - default: /* found at least one item */ - found++; - break; - } free(xdg_config); free(user_config); free(repo_config); - return ret == 0 ? found : ret; + return ret; } int git_config_with_options(config_fn_t fn, void *data, @@ -1272,7 +1256,7 @@ static void git_config_raw(config_fn_t fn, void *data) if (git_config_with_options(fn, data, NULL, 1) < 0) /* * git_config_with_options() normally returns only - * positive values, as most errors are fatal, and + * zero, as most errors are fatal, and * non-fatal potential errors are guarded by "if" * statements that are entered only when no error is * possible. -- cgit v1.2.1 From a77d6db69b2cbd16e98763f33ceaa76ff5ca2c54 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 18 May 2016 18:39:49 -0400 Subject: git_config_parse_parameter: refactor cleanup code We have several exits from the function, each of which has to do some cleanup. Let's consolidate these in an "out" label we can jump to. This doesn't save us much now, but it will help as we add more things that need cleanup. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- config.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/config.c b/config.c index 771d0ddbec..c5d0b0c7aa 100644 --- a/config.c +++ b/config.c @@ -205,6 +205,7 @@ int git_config_parse_parameter(const char *text, int git_config_from_parameters(config_fn_t fn, void *data) { const char *env = getenv(CONFIG_DATA_ENVIRONMENT); + int ret = 0; char *envw; const char **argv = NULL; int nr = 0, alloc = 0; @@ -216,21 +217,21 @@ int git_config_from_parameters(config_fn_t fn, void *data) envw = xstrdup(env); if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) { - free(envw); - return error("bogus format in " CONFIG_DATA_ENVIRONMENT); + ret = error("bogus format in " CONFIG_DATA_ENVIRONMENT); + goto out; } for (i = 0; i < nr; i++) { if (git_config_parse_parameter(argv[i], fn, data) < 0) { - free(argv); - free(envw); - return -1; + ret = -1; + goto out; } } +out: free(argv); free(envw); - return 0; + return ret; } static int get_next_char(void) -- cgit v1.2.1 From 3258258f51f45efeff5ce0e9f825f347a1404efa Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 18 May 2016 18:41:08 -0400 Subject: config: set up config_source for command-line config When we parse a config file, we set up the global "cf" variable as a pointer to a "struct config_source" describing the file we are parsing. This is used for error messages, as well as for lookup functions like current_config_name(). The "cf" variable is NULL in two cases: 1. When we are parsing command-line config, in which case there is no source file. 2. When we are not parsing any config at all. Callers like current_config_name() must assume we are in case 1 if they see a NULL "cf". However, this means that if they are accidentally used outside of a config parsing callback, they will quietly return a bogus answer. This might seem like an unlikely accident (why would you ask for the current config file if you are not parsing config?), but it's actually an easy mistake to make due to the configset caching. git_config() serves the answers from a configset cache, and any calls to current_config_name() will claim that we are parsing command-line config, no matter what the original source. So let's distinguish these cases by having the command-line config parser set up a config_source with a NULL name (which callers already handle properly). We can use this to catch programming errors in some cases, and to give better messages to the user in others. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- config.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/config.c b/config.c index c5d0b0c7aa..571151f3e4 100644 --- a/config.c +++ b/config.c @@ -131,7 +131,9 @@ static int handle_path_include(const char *path, struct config_include_data *inc if (!access_or_die(path, R_OK, 0)) { if (++inc->depth > MAX_INCLUDE_DEPTH) die(include_depth_advice, MAX_INCLUDE_DEPTH, path, - cf && cf->name ? cf->name : "the command line"); + !cf ? "" : + cf->name ? cf->name : + "the command line"); ret = git_config_from_file(git_config_include, path, inc); inc->depth--; } @@ -210,9 +212,15 @@ int git_config_from_parameters(config_fn_t fn, void *data) const char **argv = NULL; int nr = 0, alloc = 0; int i; + struct config_source source; if (!env) return 0; + + memset(&source, 0, sizeof(source)); + source.prev = cf; + cf = &source; + /* sq_dequote will write over it */ envw = xstrdup(env); @@ -231,6 +239,7 @@ int git_config_from_parameters(config_fn_t fn, void *data) out: free(argv); free(envw); + cf = source.prev; return ret; } @@ -1341,7 +1350,9 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha l_item->e = e; l_item->value_index = e->value_list.nr - 1; - if (cf) { + if (!cf) + die("BUG: configset_add_value has no source"); + if (cf->name) { kv_info->filename = strintern(cf->name); kv_info->linenr = cf->linenr; } else { @@ -2427,10 +2438,14 @@ int parse_config_key(const char *var, const char *current_config_origin_type(void) { - return cf && cf->origin_type ? cf->origin_type : "command line"; + if (!cf) + die("BUG: current_config_origin_type called outside config callback"); + return cf->origin_type ? cf->origin_type : "command line"; } const char *current_config_name(void) { - return cf && cf->name ? cf->name : ""; + if (!cf) + die("BUG: current_config_name called outside config callback"); + return cf->name ? cf->name : ""; } -- cgit v1.2.1 From 74c682d3c63bff8860af5caf9bd38a5413568709 Mon Sep 17 00:00:00 2001 From: Elia Pinto Date: Mon, 23 May 2016 13:44:02 +0000 Subject: http.c: implement the GIT_TRACE_CURL environment variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the GIT_TRACE_CURL environment variable to allow a greater degree of detail of GIT_CURL_VERBOSE, in particular the complete transport header and all the data payload exchanged. It might be useful if a particular situation could require a more thorough debugging analysis. Document the new GIT_TRACE_CURL environment variable. Helped-by: Torsten Bögershausen Helped-by: Ramsay Jones Helped-by: Junio C Hamano Helped-by: Eric Sunshine Helped-by: Jeff King Signed-off-by: Elia Pinto Signed-off-by: Junio C Hamano --- Documentation/git.txt | 8 ++++ http.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++- http.h | 2 + 3 files changed, 132 insertions(+), 2 deletions(-) diff --git a/Documentation/git.txt b/Documentation/git.txt index 8afe349781..958db0fc65 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -1075,6 +1075,14 @@ of clones and fetches. cloning of shallow repositories. See 'GIT_TRACE' for available trace output options. +'GIT_TRACE_CURL':: + Enables a curl full trace dump of all incoming and outgoing data, + including descriptive information, of the git transport protocol. + This is similar to doing curl --trace-ascii on the command line. + This option overrides setting the GIT_CURL_VERBOSE environment + variable. + See 'GIT_TRACE' for available trace output options. + 'GIT_LITERAL_PATHSPECS':: Setting this variable to `1` will cause Git to treat all pathspecs literally, rather than as glob patterns. For example, diff --git a/http.c b/http.c index 69da4454d8..deaf65522f 100644 --- a/http.c +++ b/http.c @@ -11,6 +11,7 @@ #include "gettext.h" #include "transport.h" +static struct trace_key trace_curl = TRACE_KEY_INIT(CURL); #if LIBCURL_VERSION_NUM >= 0x070a08 long int git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER; #else @@ -464,6 +465,125 @@ static void set_curl_keepalive(CURL *c) } #endif +static void redact_sensitive_header(struct strbuf *header) +{ + const char *sensitive_header; + + if (skip_prefix(header->buf, "Authorization:", &sensitive_header) || + skip_prefix(header->buf, "Proxy-Authorization:", &sensitive_header)) { + /* The first token is the type, which is OK to log */ + while (isspace(*sensitive_header)) + sensitive_header++; + while (*sensitive_header && !isspace(*sensitive_header)) + sensitive_header++; + /* Everything else is opaque and possibly sensitive */ + strbuf_setlen(header, sensitive_header - header->buf); + strbuf_addstr(header, " "); + } +} + +static void curl_dump_header(const char *text, unsigned char *ptr, size_t size, int hide_sensitive_header) +{ + struct strbuf out = STRBUF_INIT; + struct strbuf **headers, **header; + + strbuf_addf(&out, "%s, %10.10ld bytes (0x%8.8lx)\n", + text, (long)size, (long)size); + trace_strbuf(&trace_curl, &out); + strbuf_reset(&out); + strbuf_add(&out, ptr, size); + headers = strbuf_split_max(&out, '\n', 0); + + for (header = headers; *header; header++) { + if (hide_sensitive_header) + redact_sensitive_header(*header); + strbuf_insert((*header), 0, text, strlen(text)); + strbuf_insert((*header), strlen(text), ": ", 2); + strbuf_rtrim((*header)); + strbuf_addch((*header), '\n'); + trace_strbuf(&trace_curl, (*header)); + } + strbuf_list_free(headers); + strbuf_release(&out); +} + +static void curl_dump_data(const char *text, unsigned char *ptr, size_t size) +{ + size_t i; + struct strbuf out = STRBUF_INIT; + unsigned int width = 60; + + strbuf_addf(&out, "%s, %10.10ld bytes (0x%8.8lx)\n", + text, (long)size, (long)size); + trace_strbuf(&trace_curl, &out); + + for (i = 0; i < size; i += width) { + size_t w; + + strbuf_reset(&out); + strbuf_addf(&out, "%s: ", text); + for (w = 0; (w < width) && (i + w < size); w++) { + unsigned char ch = ptr[i + w]; + + strbuf_addch(&out, + (ch >= 0x20) && (ch < 0x80) + ? ch : '.'); + } + strbuf_addch(&out, '\n'); + trace_strbuf(&trace_curl, &out); + } + strbuf_release(&out); +} + +static int curl_trace(CURL *handle, curl_infotype type, char *data, size_t size, void *userp) +{ + const char *text; + enum { NO_FILTER = 0, DO_FILTER = 1 }; + + switch (type) { + case CURLINFO_TEXT: + trace_printf_key(&trace_curl, "== Info: %s", data); + default: /* we ignore unknown types by default */ + return 0; + + case CURLINFO_HEADER_OUT: + text = "=> Send header"; + curl_dump_header(text, (unsigned char *)data, size, DO_FILTER); + break; + case CURLINFO_DATA_OUT: + text = "=> Send data"; + curl_dump_data(text, (unsigned char *)data, size); + break; + case CURLINFO_SSL_DATA_OUT: + text = "=> Send SSL data"; + curl_dump_data(text, (unsigned char *)data, size); + break; + case CURLINFO_HEADER_IN: + text = "<= Recv header"; + curl_dump_header(text, (unsigned char *)data, size, NO_FILTER); + break; + case CURLINFO_DATA_IN: + text = "<= Recv data"; + curl_dump_data(text, (unsigned char *)data, size); + break; + case CURLINFO_SSL_DATA_IN: + text = "<= Recv SSL data"; + curl_dump_data(text, (unsigned char *)data, size); + break; + } + return 0; +} + +void setup_curl_trace(CURL *handle) +{ + if (!trace_want(&trace_curl)) + return; + curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, curl_trace); + curl_easy_setopt(handle, CURLOPT_DEBUGDATA, NULL); +} + + static CURL *get_curl_handle(void) { CURL *result = curl_easy_init(); @@ -562,9 +682,9 @@ static CURL *get_curl_handle(void) warning("protocol restrictions not applied to curl redirects because\n" "your curl version is too old (>= 7.19.4)"); #endif - if (getenv("GIT_CURL_VERBOSE")) - curl_easy_setopt(result, CURLOPT_VERBOSE, 1); + curl_easy_setopt(result, CURLOPT_VERBOSE, 1L); + setup_curl_trace(result); curl_easy_setopt(result, CURLOPT_USERAGENT, user_agent ? user_agent : git_user_agent()); diff --git a/http.h b/http.h index 4ef4bbda7d..2fb56d0743 100644 --- a/http.h +++ b/http.h @@ -224,4 +224,6 @@ extern int finish_http_object_request(struct http_object_request *freq); extern void abort_http_object_request(struct http_object_request *freq); extern void release_http_object_request(struct http_object_request *freq); +/* setup routine for curl_easy_setopt CURLOPT_DEBUGFUNCTION */ +void setup_curl_trace(CURL *handle); #endif /* HTTP_H */ -- cgit v1.2.1 From 73e57aaf4ded85bb5387365f1ebefaabf80cf073 Mon Sep 17 00:00:00 2001 From: Elia Pinto Date: Mon, 23 May 2016 13:44:03 +0000 Subject: imap-send.c: introduce the GIT_TRACE_CURL enviroment variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Permit the use of the GIT_TRACE_CURL environment variable calling the setup_curl_trace http.c helper routine. Helped-by: Torsten Bögershausen Helped-by: Ramsay Jones Helped-by: Junio C Hamano Helped-by: Eric Sunshine Helped-by: Jeff King Signed-off-by: Elia Pinto Signed-off-by: Junio C Hamano --- imap-send.c | 1 + 1 file changed, 1 insertion(+) diff --git a/imap-send.c b/imap-send.c index 2c52027c84..8ad80674da 100644 --- a/imap-send.c +++ b/imap-send.c @@ -1443,6 +1443,7 @@ static CURL *setup_curl(struct imap_server_conf *srvc) if (0 < verbosity || getenv("GIT_CURL_VERBOSE")) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + setup_curl_trace(curl); return curl; } -- cgit v1.2.1 From a87bcd6d47d545402ab80ae71a39531997eb6f6f Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Wed, 25 May 2016 15:00:04 -0700 Subject: submodule update: make use of the existing fetch_in_submodule function Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- git-submodule.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-submodule.sh b/git-submodule.sh index 5a4dec050b..7698102b9c 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -640,7 +640,7 @@ cmd_update() if test -z "$nofetch" then # Fetch remote before determining tracking $sha1 - (sanitize_submodule_env; cd "$sm_path" && git-fetch) || + fetch_in_submodule "$sm_path" || die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")" fi remote_name=$(sanitize_submodule_env; cd "$sm_path" && get_default_remote) -- cgit v1.2.1 From 37f52e93441e1da00c9c9824ed03cd074d77f43a Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Thu, 26 May 2016 14:59:42 -0700 Subject: submodule-config: keep shallow recommendation around The shallow field will be used in a later patch by `submodule update`. To differentiate between the actual depth (which may be different), we name it `recommend_shallow` as the field in the .gitmodules file is only a recommendation by the project. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- submodule-config.c | 9 +++++++++ submodule-config.h | 1 + 2 files changed, 10 insertions(+) diff --git a/submodule-config.c b/submodule-config.c index debab294d4..db1847ff68 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -199,6 +199,7 @@ static struct submodule *lookup_or_create_by_name(struct submodule_cache *cache, submodule->update_strategy.command = NULL; submodule->fetch_recurse = RECURSE_SUBMODULES_NONE; submodule->ignore = NULL; + submodule->recommend_shallow = -1; hashcpy(submodule->gitmodules_sha1, gitmodules_sha1); @@ -353,6 +354,14 @@ static int parse_config(const char *var, const char *value, void *data) else if (parse_submodule_update_strategy(value, &submodule->update_strategy) < 0) die(_("invalid value for %s"), var); + } else if (!strcmp(item.buf, "shallow")) { + if (!me->overwrite && submodule->recommend_shallow != -1) + warn_multiple_config(me->commit_sha1, submodule->name, + "shallow"); + else { + submodule->recommend_shallow = + git_config_bool(var, value); + } } strbuf_release(&name); diff --git a/submodule-config.h b/submodule-config.h index e4857f53a8..b1fdcc0c33 100644 --- a/submodule-config.h +++ b/submodule-config.h @@ -18,6 +18,7 @@ struct submodule { struct submodule_update_strategy update_strategy; /* the sha1 blob id of the responsible .gitmodules file */ unsigned char gitmodules_sha1[20]; + int recommend_shallow; }; int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg); -- cgit v1.2.1 From abed000acafd8aa86e02bcbb65fc1a8e4f06b8a0 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Thu, 26 May 2016 14:59:43 -0700 Subject: submodule update: learn `--[no-]recommend-shallow` option Sometimes the history of a submodule is not considered important by the projects upstream. To make it easier for downstream users, allow a boolean field 'submodule..shallow' in .gitmodules, which can be used to recommend whether upstream considers the history important. This field is honored in the initial clone by default, it can be ignored by giving the `--no-recommend-shallow` option. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- Documentation/git-submodule.txt | 11 +++++++-- builtin/submodule--helper.c | 7 +++++- git-submodule.sh | 9 ++++++- t/t5614-clone-submodules.sh | 52 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 4 deletions(-) diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 9226c4380c..bf3bb372ee 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -15,8 +15,9 @@ SYNOPSIS 'git submodule' [--quiet] init [--] [...] 'git submodule' [--quiet] deinit [-f|--force] (--all|[--] ...) 'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch] - [-f|--force] [--rebase|--merge] [--reference ] - [--depth ] [--recursive] [--jobs ] [--] [...] + [--[no-]recommend-shallow] [-f|--force] [--rebase|--merge] + [--reference ] [--depth ] [--recursive] + [--jobs ] [--] [...] 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) ] [commit] [--] [...] 'git submodule' [--quiet] foreach [--recursive] @@ -384,6 +385,12 @@ for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully. clone with a history truncated to the specified number of revisions. See linkgit:git-clone[1] +--[no-]recommend-shallow:: + This option is only valid for the update command. + The initial clone of a submodule will use the recommended + `submodule..shallow` as provided by the .gitmodules file + by default. To ignore the suggestions use `--no-recommend-shallow`. + -j :: --jobs :: This option is only valid for the update command. diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 8da263f0b0..ca33408f55 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -581,6 +581,7 @@ struct submodule_update_clone { /* configuration parameters which are passed on to the children */ int quiet; + int recommend_shallow; const char *reference; const char *depth; const char *recursive_prefix; @@ -593,7 +594,7 @@ struct submodule_update_clone { unsigned quickstop : 1; }; #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \ - SUBMODULE_UPDATE_STRATEGY_INIT, 0, NULL, NULL, NULL, NULL, \ + SUBMODULE_UPDATE_STRATEGY_INIT, 0, -1, NULL, NULL, NULL, NULL, \ STRING_LIST_INIT_DUP, 0} @@ -698,6 +699,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce, argv_array_push(&child->args, "--quiet"); if (suc->prefix) argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL); + if (suc->recommend_shallow && sub->recommend_shallow == 1) + argv_array_push(&child->args, "--depth=1"); argv_array_pushl(&child->args, "--path", sub->path, NULL); argv_array_pushl(&child->args, "--name", sub->name, NULL); argv_array_pushl(&child->args, "--url", url, NULL); @@ -780,6 +783,8 @@ static int update_clone(int argc, const char **argv, const char *prefix) "specified number of revisions")), OPT_INTEGER('j', "jobs", &max_jobs, N_("parallel jobs")), + OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow, + N_("whether the initial clone should follow the shallow recommendation")), OPT__QUIET(&suc.quiet, N_("don't print cloning progress")), OPT_END() }; diff --git a/git-submodule.sh b/git-submodule.sh index 5a4dec050b..42e0e9f63d 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -9,7 +9,7 @@ USAGE="[--quiet] add [-b ] [-f|--force] [--name ] [--reference ...] or: $dashless [--quiet] init [--] [...] or: $dashless [--quiet] deinit [-f|--force] (--all| [--] ...) - or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--reference ] [--recursive] [--] [...] + or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference ] [--recursive] [--] [...] or: $dashless [--quiet] summary [--cached|--files] [--summary-limit ] [commit] [--] [...] or: $dashless [--quiet] foreach [--recursive] or: $dashless [--quiet] sync [--recursive] [--] [...]" @@ -559,6 +559,12 @@ cmd_update() --checkout) update="checkout" ;; + --recommend-shallow) + recommend_shallow="--recommend-shallow" + ;; + --no-recommend-shallow) + recommend_shallow="--no-recommend-shallow" + ;; --depth) case "$2" in '') usage ;; esac depth="--depth=$2" @@ -601,6 +607,7 @@ cmd_update() ${update:+--update "$update"} \ ${reference:+--reference "$reference"} \ ${depth:+--depth "$depth"} \ + ${recommend_shallow:+"$recommend_shallow"} \ ${jobs:+$jobs} \ "$@" || echo "#unmatched" } | { diff --git a/t/t5614-clone-submodules.sh b/t/t5614-clone-submodules.sh index 62044c5a02..32d83e2694 100755 --- a/t/t5614-clone-submodules.sh +++ b/t/t5614-clone-submodules.sh @@ -82,4 +82,56 @@ test_expect_success 'non shallow clone with shallow submodule' ' ) ' +test_expect_success 'clone follows shallow recommendation' ' + test_when_finished "rm -rf super_clone" && + git config -f .gitmodules submodule.sub.shallow true && + git add .gitmodules && + git commit -m "recommed shallow for sub" && + git clone --recurse-submodules --no-local "file://$pwd/." super_clone && + ( + cd super_clone && + git log --oneline >lines && + test_line_count = 4 lines + ) && + ( + cd super_clone/sub && + git log --oneline >lines && + test_line_count = 1 lines + ) +' + +test_expect_success 'get unshallow recommended shallow submodule' ' + test_when_finished "rm -rf super_clone" && + git clone --no-local "file://$pwd/." super_clone && + ( + cd super_clone && + git submodule update --init --no-recommend-shallow && + git log --oneline >lines && + test_line_count = 4 lines + ) && + ( + cd super_clone/sub && + git log --oneline >lines && + test_line_count = 3 lines + ) +' + +test_expect_success 'clone follows non shallow recommendation' ' + test_when_finished "rm -rf super_clone" && + git config -f .gitmodules submodule.sub.shallow false && + git add .gitmodules && + git commit -m "recommed non shallow for sub" && + git clone --recurse-submodules --no-local "file://$pwd/." super_clone && + ( + cd super_clone && + git log --oneline >lines && + test_line_count = 5 lines + ) && + ( + cd super_clone/sub && + git log --oneline >lines && + test_line_count = 3 lines + ) +' + test_done -- cgit v1.2.1 From 0d44a2dacc84fb7dcb5d684800e976f3b3c76d00 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 26 May 2016 20:32:23 -0400 Subject: config: return configset value for current_config_ functions When 473166b (config: add 'origin_type' to config_source struct, 2016-02-19) added accessor functions for the origin type and name, it taught them only to look at the "cf" struct that is filled in while we are parsing the config. This is sufficient to make it work with git-config, which uses git_config_with_options() under the hood. That function freshly parses the config files and triggers the callback when it parses each key. Most git programs, however, use git_config(). This interface will populate a cache during the actual parse, and then serve values from the cache. Calling current_config_filename() in a callback here will find a NULL cf and produce an error. There are no such callers right now, but let's prepare for adding some by making this work. We already record source information in a struct attached to each value. We just need to make it globally available and then consult it from the accessor functions. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- cache.h | 1 + config.c | 51 +++++++++++++++++++++++++++++++++++++++++--------- t/helper/test-config.c | 20 ++++++++++++++++++++ t/t1308-config-set.sh | 24 ++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 9 deletions(-) diff --git a/cache.h b/cache.h index 6049f86711..1bce212e88 100644 --- a/cache.h +++ b/cache.h @@ -1696,6 +1696,7 @@ extern int ignore_untracked_cache_config; struct key_value_info { const char *filename; int linenr; + const char *origin_type; }; extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3))); diff --git a/config.c b/config.c index 571151f3e4..d555deeb9a 100644 --- a/config.c +++ b/config.c @@ -38,7 +38,24 @@ struct config_source { long (*do_ftell)(struct config_source *c); }; +/* + * These variables record the "current" config source, which + * can be accessed by parsing callbacks. + * + * The "cf" variable will be non-NULL only when we are actually parsing a real + * config source (file, blob, cmdline, etc). + * + * The "current_config_kvi" variable will be non-NULL only when we are feeding + * cached config from a configset into a callback. + * + * They should generally never be non-NULL at the same time. If they are both + * NULL, then we aren't parsing anything (and depending on the function looking + * at the variables, it's either a bug for it to be called in the first place, + * or it's a function which can be reused for non-config purposes, and should + * fall back to some sane behavior). + */ static struct config_source *cf; +static struct key_value_info *current_config_kvi; static int zlib_compression_seen; @@ -1284,16 +1301,20 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data) struct string_list *values; struct config_set_element *entry; struct configset_list *list = &cs->list; - struct key_value_info *kv_info; for (i = 0; i < list->nr; i++) { entry = list->items[i].e; value_index = list->items[i].value_index; values = &entry->value_list; - if (fn(entry->key, values->items[value_index].string, data) < 0) { - kv_info = values->items[value_index].util; - git_die_config_linenr(entry->key, kv_info->filename, kv_info->linenr); - } + + current_config_kvi = values->items[value_index].util; + + if (fn(entry->key, values->items[value_index].string, data) < 0) + git_die_config_linenr(entry->key, + current_config_kvi->filename, + current_config_kvi->linenr); + + current_config_kvi = NULL; } } @@ -1355,10 +1376,12 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha if (cf->name) { kv_info->filename = strintern(cf->name); kv_info->linenr = cf->linenr; + kv_info->origin_type = strintern(cf->origin_type); } else { /* for values read from `git_config_from_parameters()` */ kv_info->filename = NULL; kv_info->linenr = -1; + kv_info->origin_type = NULL; } si->util = kv_info; @@ -2438,14 +2461,24 @@ int parse_config_key(const char *var, const char *current_config_origin_type(void) { - if (!cf) + const char *type; + if (current_config_kvi) + type = current_config_kvi->origin_type; + else if(cf) + type = cf->origin_type; + else die("BUG: current_config_origin_type called outside config callback"); - return cf->origin_type ? cf->origin_type : "command line"; + return type ? type : "command line"; } const char *current_config_name(void) { - if (!cf) + const char *name; + if (current_config_kvi) + name = current_config_kvi->filename; + else if (cf) + name = cf->name; + else die("BUG: current_config_name called outside config callback"); - return cf->name ? cf->name : ""; + return name ? name : ""; } diff --git a/t/helper/test-config.c b/t/helper/test-config.c index 6a77552210..3605ef8ee6 100644 --- a/t/helper/test-config.c +++ b/t/helper/test-config.c @@ -25,6 +25,9 @@ * ascending order of priority from a config_set * constructed from files entered as arguments. * + * iterate -> iterate over all values using git_config(), and print some + * data for each + * * Examples: * * To print the value with highest priority for key "foo.bAr Baz.rock": @@ -32,6 +35,20 @@ * */ +static int iterate_cb(const char *var, const char *value, void *data) +{ + static int nr; + + if (nr++) + putchar('\n'); + + printf("key=%s\n", var); + printf("value=%s\n", value ? value : "(null)"); + printf("origin=%s\n", current_config_origin_type()); + printf("name=%s\n", current_config_name()); + + return 0; +} int main(int argc, char **argv) { @@ -134,6 +151,9 @@ int main(int argc, char **argv) printf("Value not found for \"%s\"\n", argv[2]); goto exit1; } + } else if (!strcmp(argv[1], "iterate")) { + git_config(iterate_cb, NULL); + goto exit0; } die("%s: Please check the syntax and the function name", argv[0]); diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index 005d66dbef..d345a885a3 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -229,4 +229,28 @@ test_expect_success 'error on modifying repo config without repo' ' ) ' +cmdline_config="'foo.bar=from-cmdline'" +test_expect_success 'iteration shows correct origins' ' + echo "[foo]bar = from-repo" >.git/config && + echo "[foo]bar = from-home" >.gitconfig && + cat >expect <<-EOF && + key=foo.bar + value=from-home + origin=file + name=$(pwd)/.gitconfig + + key=foo.bar + value=from-repo + origin=file + name=.git/config + + key=foo.bar + value=from-cmdline + origin=command line + name= + EOF + GIT_CONFIG_PARAMETERS=$cmdline_config test-config iterate >actual && + test_cmp expect actual +' + test_done -- cgit v1.2.1 From 9acc5911119ec0209877fbaa0a1e68aa714c191e Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 18 May 2016 18:44:23 -0400 Subject: config: add a notion of "scope" A config callback passed to git_config() doesn't know very much about the context in which it sees a variable. It can ask whether the variable comes from a file, and get the file name. But without analyzing the filename (which is hard to do accurately), it cannot tell whether it is in system-level config, user-level config, or repo-specific config. Generally this doesn't matter; the point of not passing this to the callback is that it should treat the config the same no matter where it comes from. But some programs, like upload-pack, are a special case: we should be able to run them in an untrusted repository, which means we cannot use any "dangerous" config from the repository config file (but it is OK to use it from system or user config). This patch teaches the config code to record the "scope" of each variable, and make it available inside config callbacks, similar to how we give access to the filename. The scope is the starting source for a particular parsing operation, and remains the same even if we include other files (so a .git/config which includes another file will remain CONFIG_SCOPE_REPO, as it would be similarly untrusted). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- cache.h | 11 +++++++++++ config.c | 23 +++++++++++++++++++++++ t/helper/test-config.c | 16 ++++++++++++++++ t/t1308-config-set.sh | 3 +++ 4 files changed, 53 insertions(+) diff --git a/cache.h b/cache.h index 1bce212e88..2e3b377ae8 100644 --- a/cache.h +++ b/cache.h @@ -1604,6 +1604,16 @@ extern const char *get_log_output_encoding(void); extern const char *get_commit_output_encoding(void); extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data); + +enum config_scope { + CONFIG_SCOPE_UNKNOWN = 0, + CONFIG_SCOPE_SYSTEM, + CONFIG_SCOPE_GLOBAL, + CONFIG_SCOPE_REPO, + CONFIG_SCOPE_CMDLINE, +}; + +extern enum config_scope current_config_scope(void); extern const char *current_config_origin_type(void); extern const char *current_config_name(void); @@ -1697,6 +1707,7 @@ struct key_value_info { const char *filename; int linenr; const char *origin_type; + enum config_scope scope; }; extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3))); diff --git a/config.c b/config.c index d555deeb9a..87629164b3 100644 --- a/config.c +++ b/config.c @@ -57,6 +57,15 @@ struct config_source { static struct config_source *cf; static struct key_value_info *current_config_kvi; +/* + * Similar to the variables above, this gives access to the "scope" of the + * current value (repo, global, etc). For cached values, it can be found via + * the current_config_kvi as above. During parsing, the current value can be + * found in this variable. It's not part of "cf" because it transcends a single + * file (i.e., a file included from .git/config is still in "repo" scope). + */ +static enum config_scope current_parsing_scope; + static int zlib_compression_seen; /* @@ -1229,22 +1238,27 @@ static int do_git_config_sequence(config_fn_t fn, void *data) char *user_config = expand_user_path("~/.gitconfig"); char *repo_config = git_pathdup("config"); + current_parsing_scope = CONFIG_SCOPE_SYSTEM; if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) ret += git_config_from_file(fn, git_etc_gitconfig(), data); + current_parsing_scope = CONFIG_SCOPE_GLOBAL; if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) ret += git_config_from_file(fn, xdg_config, data); if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) ret += git_config_from_file(fn, user_config, data); + current_parsing_scope = CONFIG_SCOPE_REPO; if (repo_config && !access_or_die(repo_config, R_OK, 0)) ret += git_config_from_file(fn, repo_config, data); + current_parsing_scope = CONFIG_SCOPE_CMDLINE; if (git_config_from_parameters(fn, data) < 0) die(_("unable to parse command-line config")); + current_parsing_scope = CONFIG_SCOPE_UNKNOWN; free(xdg_config); free(user_config); free(repo_config); @@ -1383,6 +1397,7 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha kv_info->linenr = -1; kv_info->origin_type = NULL; } + kv_info->scope = current_parsing_scope; si->util = kv_info; return 0; @@ -2482,3 +2497,11 @@ const char *current_config_name(void) die("BUG: current_config_name called outside config callback"); return name ? name : ""; } + +enum config_scope current_config_scope(void) +{ + if (current_config_kvi) + return current_config_kvi->scope; + else + return current_parsing_scope; +} diff --git a/t/helper/test-config.c b/t/helper/test-config.c index 3605ef8ee6..509aeef400 100644 --- a/t/helper/test-config.c +++ b/t/helper/test-config.c @@ -35,6 +35,21 @@ * */ +static const char *scope_name(enum config_scope scope) +{ + switch (scope) { + case CONFIG_SCOPE_SYSTEM: + return "system"; + case CONFIG_SCOPE_GLOBAL: + return "global"; + case CONFIG_SCOPE_REPO: + return "repo"; + case CONFIG_SCOPE_CMDLINE: + return "cmdline"; + default: + return "unknown"; + } +} static int iterate_cb(const char *var, const char *value, void *data) { static int nr; @@ -46,6 +61,7 @@ static int iterate_cb(const char *var, const char *value, void *data) printf("value=%s\n", value ? value : "(null)"); printf("origin=%s\n", current_config_origin_type()); printf("name=%s\n", current_config_name()); + printf("scope=%s\n", scope_name(current_config_scope())); return 0; } diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index d345a885a3..065d5ebb09 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -238,16 +238,19 @@ test_expect_success 'iteration shows correct origins' ' value=from-home origin=file name=$(pwd)/.gitconfig + scope=global key=foo.bar value=from-repo origin=file name=.git/config + scope=repo key=foo.bar value=from-cmdline origin=command line name= + scope=cmdline EOF GIT_CONFIG_PARAMETERS=$cmdline_config test-config iterate >actual && test_cmp expect actual -- cgit v1.2.1 From d2986d0f290a065fb8a534fabfff36c40d37ae97 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 25 May 2016 22:54:02 +0000 Subject: fast-import: invalidate pack_id references after loosening When loosening a pack, the current pack_id gets reused when checkpointing and the import does not terminate. This causes problems after checkpointing as the object table, branch, and tag lists still contains pre-checkpoint references to the recycled pack_id. Merely clearing the object_table as suggested by Jeff King in http://mid.gmane.org/20160517121330.GA7346@sigill.intra.peff.net is insufficient as the marks set still contains references to object entries. Wrong pack_id references branch and tags lists do not cause errors, but can lead to misleading crash reports and core dumps, so they are also invalidated. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- fast-import.c | 31 +++++++++++++++++++- t/t9302-fast-import-unpack-limit.sh | 57 +++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/fast-import.c b/fast-import.c index 4fb464c1ef..e415f517f8 100644 --- a/fast-import.c +++ b/fast-import.c @@ -597,6 +597,33 @@ static struct object_entry *insert_object(unsigned char *sha1) return e; } +static void invalidate_pack_id(unsigned int id) +{ + unsigned int h; + unsigned long lu; + struct tag *t; + + for (h = 0; h < ARRAY_SIZE(object_table); h++) { + struct object_entry *e; + + for (e = object_table[h]; e; e = e->next) + if (e->pack_id == id) + e->pack_id = MAX_PACK_ID; + } + + for (lu = 0; lu < branch_table_sz; lu++) { + struct branch *b; + + for (b = branch_table[lu]; b; b = b->table_next_branch) + if (b->pack_id == id) + b->pack_id = MAX_PACK_ID; + } + + for (t = first_tag; t; t = t->next_tag) + if (t->pack_id == id) + t->pack_id = MAX_PACK_ID; +} + static unsigned int hc_str(const char *s, size_t len) { unsigned int r = 0; @@ -993,8 +1020,10 @@ static void end_packfile(void) cur_pack_sha1, pack_size); if (object_count <= unpack_limit) { - if (!loosen_small_pack(pack_data)) + if (!loosen_small_pack(pack_data)) { + invalidate_pack_id(pack_id); goto discard_pack; + } } close(pack_data->pack_fd); diff --git a/t/t9302-fast-import-unpack-limit.sh b/t/t9302-fast-import-unpack-limit.sh index 0f686d2199..a04de14677 100755 --- a/t/t9302-fast-import-unpack-limit.sh +++ b/t/t9302-fast-import-unpack-limit.sh @@ -45,4 +45,61 @@ test_expect_success 'bigger packs are preserved' ' test $(find .git/objects/pack -type f | wc -l) -eq 2 ' +test_expect_success 'lookups after checkpoint works' ' + hello_id=$(echo hello | git hash-object --stdin -t blob) && + id="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE" && + before=$(git rev-parse refs/heads/master^0) && + ( + cat <<-INPUT_END && + blob + mark :1 + data 6 + hello + + commit refs/heads/master + mark :2 + committer $id + data <&2 "checkpoint did not update branch" + exit 1 + else + n=$(($n + 1)) + fi && + sleep 1 && + from=$(git rev-parse refs/heads/master^0) + done && + cat <<-INPUT_END && + commit refs/heads/master + committer $id + data < Date: Tue, 31 May 2016 11:57:08 +0200 Subject: upload-pack.c: use parse-options API Use the parse-options API rather than a hand-rolled option parser. Description for --stateless-rpc and --advertise-refs come from 42526b4 (Add stateless RPC options to upload-pack, receive-pack, 2009-10-30). Signed-off-by: Antoine Queru Signed-off-by: Matthieu Moy Signed-off-by: Junio C Hamano --- Documentation/git-upload-pack.txt | 16 ++++++++--- upload-pack.c | 57 +++++++++++++++++---------------------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt index 0abc806ea9..822ad593af 100644 --- a/Documentation/git-upload-pack.txt +++ b/Documentation/git-upload-pack.txt @@ -9,8 +9,8 @@ git-upload-pack - Send objects packed back to git-fetch-pack SYNOPSIS -------- [verse] -'git-upload-pack' [--strict] [--timeout=] - +'git-upload-pack' [--[no-]strict] [--timeout=] [--stateless-rpc] + [--advertise-refs] DESCRIPTION ----------- Invoked by 'git fetch-pack', learns what @@ -25,12 +25,22 @@ repository. For push operations, see 'git send-pack'. OPTIONS ------- ---strict:: +--[no-]strict:: Do not try /.git/ if is no Git directory. --timeout=:: Interrupt transfer after seconds of inactivity. +--stateless-rpc:: + Perform only a single read-write cycle with stdin and stdout. + This fits with the HTTP POST request processing model where + a program may read the request, write a response, and must exit. + +--advertise-refs:: + Only the initial ref advertisement is output, and the program exits + immediately. This fits with the HTTP GET request model, where + no request content is received but a response must be produced. + :: The repository to sync from. diff --git a/upload-pack.c b/upload-pack.c index f19444df7b..9e03c278b9 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -14,8 +14,12 @@ #include "sigchain.h" #include "version.h" #include "string-list.h" +#include "parse-options.h" -static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=] "; +static const char * const upload_pack_usage[] = { + N_("git upload-pack [] "), + NULL +}; /* Remember to update object flag allocation in object.h */ #define THEY_HAVE (1u << 11) @@ -816,11 +820,21 @@ static int upload_pack_config(const char *var, const char *value, void *unused) return parse_hide_refs_config(var, value, "uploadpack"); } -int main(int argc, char **argv) +int main(int argc, const char **argv) { - char *dir; - int i; + const char *dir; int strict = 0; + struct option options[] = { + OPT_BOOL(0, "stateless-rpc", &stateless_rpc, + N_("quit after a single request/response exchange")), + OPT_BOOL(0, "advertise-refs", &advertise_refs, + N_("exit immediately after intial ref advertisement")), + OPT_BOOL(0, "strict", &strict, + N_("do not try /.git/ if is no Git directory")), + OPT_INTEGER(0, "timeout", &timeout, + N_("interrupt transfer after seconds of inactivity")), + OPT_END() + }; git_setup_gettext(); @@ -828,40 +842,17 @@ int main(int argc, char **argv) git_extract_argv0_path(argv[0]); check_replace_refs = 0; - for (i = 1; i < argc; i++) { - char *arg = argv[i]; + argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0); - if (arg[0] != '-') - break; - if (!strcmp(arg, "--advertise-refs")) { - advertise_refs = 1; - continue; - } - if (!strcmp(arg, "--stateless-rpc")) { - stateless_rpc = 1; - continue; - } - if (!strcmp(arg, "--strict")) { - strict = 1; - continue; - } - if (starts_with(arg, "--timeout=")) { - timeout = atoi(arg+10); - daemon_mode = 1; - continue; - } - if (!strcmp(arg, "--")) { - i++; - break; - } - } + if (argc != 1) + usage_with_options(upload_pack_usage, options); - if (i != argc-1) - usage(upload_pack_usage); + if (timeout) + daemon_mode = 1; setup_path(); - dir = argv[i]; + dir = argv[0]; if (!enter_repo(dir, strict)) die("'%s' does not appear to be a git repository", dir); -- cgit v1.2.1 From 1a450e2fd1f82311b214851d5b097b74c8fb0ade Mon Sep 17 00:00:00 2001 From: Jordan DE GEA Date: Fri, 27 May 2016 15:17:08 +0200 Subject: worktree: allow "-" short-hand for @{-1} in add command Since `git worktree add` uses `git checkout` when `[]` is used, and `git checkout -` is already supported, it makes sense to allow the same shortcut in `git worktree add`. Signed-off-by: Jordan DE GEA Signed-off-by: Matthieu Moy Signed-off-by: Junio C Hamano --- Documentation/git-worktree.txt | 3 ++- builtin/worktree.c | 3 +++ t/t2025-worktree-add.sh | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt index c62234538b..23d8d2ace0 100644 --- a/Documentation/git-worktree.txt +++ b/Documentation/git-worktree.txt @@ -48,7 +48,8 @@ add []:: Create `` and checkout `` into it. The new working directory is linked to the current repository, sharing everything except working -directory specific files such as HEAD, index, etc. +directory specific files such as HEAD, index, etc. `-` may also be +specified as ``; it is synonymous with `@{-1}`. + If `` is omitted and neither `-b` nor `-B` nor `--detached` used, then, as a convenience, a new branch based at HEAD is created automatically, diff --git a/builtin/worktree.c b/builtin/worktree.c index 96a2834a18..e3199a22e5 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -340,6 +340,9 @@ static int add(int ac, const char **av, const char *prefix) path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0]; branch = ac < 2 ? "HEAD" : av[1]; + if (!strcmp(branch, "-")) + branch = "@{-1}"; + opts.force_new_branch = !!new_branch_force; if (opts.force_new_branch) { struct strbuf symref = STRBUF_INIT; diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh index 3a22fc55fc..4bcc335a19 100755 --- a/t/t2025-worktree-add.sh +++ b/t/t2025-worktree-add.sh @@ -20,6 +20,22 @@ test_expect_success '"add" an existing empty worktree' ' git worktree add --detach existing_empty master ' +test_expect_success '"add" using shorthand - fails when no previous branch' ' + test_must_fail git worktree add existing_short - +' + +test_expect_success '"add" using - shorthand' ' + git checkout -b newbranch && + echo hello >myworld && + git add myworld && + git commit -m myworld && + git checkout master && + git worktree add short-hand - && + echo refs/heads/newbranch >expect && + git -C short-hand rev-parse --symbolic-full-name HEAD >actual && + test_cmp expect actual +' + test_expect_success '"add" refuses to checkout locked branch' ' test_must_fail git worktree add zere master && ! test -d zere && -- cgit v1.2.1 From 6f27b941f2664c1653d0c4edcec5686119f0c023 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:46 +0200 Subject: builtin/apply: move 'state' init into init_apply_state() When the apply functionality will be libified, the 'struct apply_state' will be used by different pieces of code. To properly initialize a 'struct apply_state', let's provide a nice and easy to use init_apply_state() function. Let's also provide clear_apply_state() to release memory used by 'struct apply_state' members, so that a 'struct apply_state' instance can be easily reused without leaking memory. Note that clear_apply_state() does nothing for now, but it will later. While at it, let's rename 'prefix_' parameter to 'prefix'. Helped-by: Eric Sunshine Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index ae068e7392..52b5d3ed8e 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4522,7 +4522,25 @@ static int option_parse_directory(const struct option *opt, return 0; } -int cmd_apply(int argc, const char **argv, const char *prefix_) +static void init_apply_state(struct apply_state *state, const char *prefix) +{ + memset(state, 0, sizeof(*state)); + state->prefix = prefix; + state->prefix_length = state->prefix ? strlen(state->prefix) : 0; + + git_apply_config(); + if (apply_default_whitespace) + parse_whitespace_option(apply_default_whitespace); + if (apply_default_ignorewhitespace) + parse_ignorewhitespace_option(apply_default_ignorewhitespace); +} + +static void clear_apply_state(struct apply_state *state) +{ + /* empty for now */ +} + +int cmd_apply(int argc, const char **argv, const char *prefix) { int i; int errs = 0; @@ -4603,15 +4621,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) OPT_END() }; - memset(&state, 0, sizeof(state)); - state.prefix = prefix_; - state.prefix_length = state.prefix ? strlen(state.prefix) : 0; - - git_apply_config(); - if (apply_default_whitespace) - parse_whitespace_option(apply_default_whitespace); - if (apply_default_ignorewhitespace) - parse_ignorewhitespace_option(apply_default_ignorewhitespace); + init_apply_state(&state, prefix); argc = parse_options(argc, argv, state.prefix, builtin_apply_options, apply_usage, 0); @@ -4695,5 +4705,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) die(_("Unable to write new index file")); } + clear_apply_state(&state); + return !!errs; } -- cgit v1.2.1 From 1da16e1ed85c5be6eb243ca91225757b25088623 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:47 +0200 Subject: builtin/apply: move 'unidiff_zero' global into 'struct apply_state' To libify the apply functionality the 'unidiff_zero' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 52b5d3ed8e..6c36898904 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -24,6 +24,9 @@ struct apply_state { const char *prefix; int prefix_length; + + /* These boolean parameters control how the apply is done */ + int unidiff_zero; }; /* @@ -37,7 +40,6 @@ struct apply_state { */ static int newfd = -1; -static int unidiff_zero; static int state_p_value = 1; static int p_value_known; static int check_index; @@ -2694,7 +2696,8 @@ static void update_image(struct image *img, * postimage) for the hunk. Find lines that match "preimage" in "img" and * replace the part of "img" with "postimage" text. */ -static int apply_one_fragment(struct image *img, struct fragment *frag, +static int apply_one_fragment(struct apply_state *state, + struct image *img, struct fragment *frag, int inaccurate_eof, unsigned ws_rule, int nth_fragment) { @@ -2836,7 +2839,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, * without leading context must match at the beginning. */ match_beginning = (!frag->oldpos || - (frag->oldpos == 1 && !unidiff_zero)); + (frag->oldpos == 1 && !state->unidiff_zero)); /* * A hunk without trailing lines must match at the end. @@ -2844,7 +2847,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag, * from the lack of trailing lines if the patch was generated * with unidiff without any context. */ - match_end = !unidiff_zero && !trailing; + match_end = !state->unidiff_zero && !trailing; pos = frag->newpos ? (frag->newpos - 1) : 0; preimage.buf = oldlines; @@ -3067,7 +3070,7 @@ static int apply_binary(struct image *img, struct patch *patch) return 0; } -static int apply_fragments(struct image *img, struct patch *patch) +static int apply_fragments(struct apply_state *state, struct image *img, struct patch *patch) { struct fragment *frag = patch->fragments; const char *name = patch->old_name ? patch->old_name : patch->new_name; @@ -3080,7 +3083,7 @@ static int apply_fragments(struct image *img, struct patch *patch) while (frag) { nth++; - if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule, nth)) { + if (apply_one_fragment(state, img, frag, inaccurate_eof, ws_rule, nth)) { error(_("patch failed: %s:%ld"), name, frag->oldpos); if (!apply_with_reject) return -1; @@ -3388,8 +3391,11 @@ static int load_current(struct image *image, struct patch *patch) return 0; } -static int try_threeway(struct image *image, struct patch *patch, - struct stat *st, const struct cache_entry *ce) +static int try_threeway(struct apply_state *state, + struct image *image, + struct patch *patch, + struct stat *st, + const struct cache_entry *ce) { unsigned char pre_sha1[20], post_sha1[20], our_sha1[20]; struct strbuf buf = STRBUF_INIT; @@ -3415,7 +3421,7 @@ static int try_threeway(struct image *image, struct patch *patch, img = strbuf_detach(&buf, &len); prepare_image(&tmp_image, img, len, 1); /* Apply the patch to get the post image */ - if (apply_fragments(&tmp_image, patch) < 0) { + if (apply_fragments(state, &tmp_image, patch) < 0) { clear_image(&tmp_image); return -1; } @@ -3459,7 +3465,8 @@ static int try_threeway(struct image *image, struct patch *patch, return 0; } -static int apply_data(struct patch *patch, struct stat *st, const struct cache_entry *ce) +static int apply_data(struct apply_state *state, struct patch *patch, + struct stat *st, const struct cache_entry *ce) { struct image image; @@ -3467,9 +3474,9 @@ static int apply_data(struct patch *patch, struct stat *st, const struct cache_e return -1; if (patch->direct_to_threeway || - apply_fragments(&image, patch) < 0) { + apply_fragments(state, &image, patch) < 0) { /* Note: with --reject, apply_fragments() returns 0 */ - if (!threeway || try_threeway(&image, patch, st, ce) < 0) + if (!threeway || try_threeway(state, &image, patch, st, ce) < 0) return -1; } patch->result = image.buf; @@ -3717,7 +3724,7 @@ static void die_on_unsafe_path(struct patch *patch) * Check and apply the patch in-core; leave the result in patch->result * for the caller to write it out to the final destination. */ -static int check_patch(struct patch *patch) +static int check_patch(struct apply_state *state, struct patch *patch) { struct stat st; const char *old_name = patch->old_name; @@ -3816,13 +3823,13 @@ static int check_patch(struct patch *patch) return error(_("affected file '%s' is beyond a symbolic link"), patch->new_name); - if (apply_data(patch, &st, ce) < 0) + if (apply_data(state, patch, &st, ce) < 0) return error(_("%s: patch does not apply"), name); patch->rejected = 0; return 0; } -static int check_patch_list(struct patch *patch) +static int check_patch_list(struct apply_state *state, struct patch *patch) { int err = 0; @@ -3832,7 +3839,7 @@ static int check_patch_list(struct patch *patch) if (apply_verbosely) say_patch_name(stderr, _("Checking patch %s..."), patch); - err |= check_patch(patch); + err |= check_patch(state, patch); patch = patch->next; } return err; @@ -4434,7 +4441,7 @@ static int apply_patch(struct apply_state *state, } if ((check || apply) && - check_patch_list(list) < 0 && + check_patch_list(state, list) < 0 && !apply_with_reject) exit(1); @@ -4602,7 +4609,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) PARSE_OPT_NOARG, option_parse_space_change }, OPT_BOOL('R', "reverse", &apply_in_reverse, N_("apply the patch in reverse")), - OPT_BOOL(0, "unidiff-zero", &unidiff_zero, + OPT_BOOL(0, "unidiff-zero", &state.unidiff_zero, N_("don't expect at least one line of context")), OPT_BOOL(0, "reject", &apply_with_reject, N_("leave the rejected hunks in corresponding *.rej files")), -- cgit v1.2.1 From 22a7233584b1983e2a7337d3b0f2c13f0cc34651 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:48 +0200 Subject: builtin/apply: move 'check' global into 'struct apply_state' To libify the apply functionality the 'check' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 6c36898904..55a5541fde 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -25,13 +25,14 @@ struct apply_state { const char *prefix; int prefix_length; + /* These control what gets looked at and modified */ + int check; /* preimage must match working tree, don't actually apply */ + /* These boolean parameters control how the apply is done */ int unidiff_zero; }; /* - * --check turns on checking that the working tree matches the - * files that are being modified, but doesn't apply the patch * --stat does just a diffstat, and doesn't actually apply * --numstat does numeric diffstat, and doesn't actually apply * --index-info shows the old and new index info for paths if available. @@ -48,7 +49,6 @@ static int cached; static int diffstat; static int numstat; static int summary; -static int check; static int apply = 1; static int apply_in_reverse; static int apply_with_reject; @@ -2053,7 +2053,7 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si * without metadata change. A binary patch appears * empty to us here. */ - if ((apply || check) && + if ((apply || state->check) && (!patch->is_binary && !metadata_changes(patch))) die(_("patch with only garbage at line %d"), state_linenr); } @@ -4440,7 +4440,7 @@ static int apply_patch(struct apply_state *state, die(_("unable to read index file")); } - if ((check || apply) && + if ((state->check || apply) && check_patch_list(state, list) < 0 && !apply_with_reject) exit(1); @@ -4579,7 +4579,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) N_("show number of added and deleted lines in decimal notation")), OPT_BOOL(0, "summary", &summary, N_("instead of applying the patch, output a summary for the input")), - OPT_BOOL(0, "check", &check, + OPT_BOOL(0, "check", &state.check, N_("instead of applying the patch, see if the patch is applicable")), OPT_BOOL(0, "index", &check_index, N_("make sure the patch is applicable to the current index")), @@ -4644,7 +4644,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) } if (apply_with_reject) apply = apply_verbosely = 1; - if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor)) + if (!force_apply && (diffstat || numstat || summary || state.check || fake_ancestor)) apply = 0; if (check_index && is_not_gitdir) die(_("--index outside a repository")); -- cgit v1.2.1 From ee87a6e7404549d2be738bd54f2f191e8b5ea352 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:49 +0200 Subject: builtin/apply: move 'check_index' global into 'struct apply_state' To libify the apply functionality the 'check_index' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 66 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 55a5541fde..769383cd5d 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -27,6 +27,7 @@ struct apply_state { /* These control what gets looked at and modified */ int check; /* preimage must match working tree, don't actually apply */ + int check_index; /* preimage must match the indexed version */ /* These boolean parameters control how the apply is done */ int unidiff_zero; @@ -36,14 +37,12 @@ struct apply_state { * --stat does just a diffstat, and doesn't actually apply * --numstat does numeric diffstat, and doesn't actually apply * --index-info shows the old and new index info for paths if available. - * --index updates the cache as well. * --cached updates only the cache without ever touching the working tree. */ static int newfd = -1; static int state_p_value = 1; static int p_value_known; -static int check_index; static int update_index; static int cached; static int diffstat; @@ -3245,13 +3244,14 @@ static int verify_index_match(const struct cache_entry *ce, struct stat *st) #define SUBMODULE_PATCH_WITHOUT_INDEX 1 -static int load_patch_target(struct strbuf *buf, +static int load_patch_target(struct apply_state *state, + struct strbuf *buf, const struct cache_entry *ce, struct stat *st, const char *name, unsigned expected_mode) { - if (cached || check_index) { + if (cached || state->check_index) { if (read_file_or_gitlink(ce, buf)) return error(_("read of %s failed"), name); } else if (name) { @@ -3277,7 +3277,8 @@ static int load_patch_target(struct strbuf *buf, * applying a non-git patch that incrementally updates the tree, * we read from the result of a previous diff. */ -static int load_preimage(struct image *image, +static int load_preimage(struct apply_state *state, + struct image *image, struct patch *patch, struct stat *st, const struct cache_entry *ce) { @@ -3295,7 +3296,7 @@ static int load_preimage(struct image *image, /* We have a patched copy in memory; use that. */ strbuf_add(&buf, previous->result, previous->resultsize); } else { - status = load_patch_target(&buf, ce, st, + status = load_patch_target(state, &buf, ce, st, patch->old_name, patch->old_mode); if (status < 0) return status; @@ -3354,7 +3355,9 @@ static int three_way_merge(struct image *image, * the current contents of the new_name. In no cases other than that * this function will be called. */ -static int load_current(struct image *image, struct patch *patch) +static int load_current(struct apply_state *state, + struct image *image, + struct patch *patch) { struct strbuf buf = STRBUF_INIT; int status, pos; @@ -3381,7 +3384,7 @@ static int load_current(struct image *image, struct patch *patch) if (verify_index_match(ce, &st)) return error(_("%s: does not match index"), name); - status = load_patch_target(&buf, ce, &st, name, mode); + status = load_patch_target(state, &buf, ce, &st, name, mode); if (status < 0) return status; else if (status) @@ -3431,11 +3434,11 @@ static int try_threeway(struct apply_state *state, /* our_sha1[] is ours */ if (patch->is_new) { - if (load_current(&tmp_image, patch)) + if (load_current(state, &tmp_image, patch)) return error("cannot read the current contents of '%s'", patch->new_name); } else { - if (load_preimage(&tmp_image, patch, st, ce)) + if (load_preimage(state, &tmp_image, patch, st, ce)) return error("cannot read the current contents of '%s'", patch->old_name); } @@ -3470,7 +3473,7 @@ static int apply_data(struct apply_state *state, struct patch *patch, { struct image image; - if (load_preimage(&image, patch, st, ce) < 0) + if (load_preimage(state, &image, patch, st, ce) < 0) return -1; if (patch->direct_to_threeway || @@ -3501,7 +3504,10 @@ static int apply_data(struct apply_state *state, struct patch *patch, * check_patch() separately makes sure (and errors out otherwise) that * the path the patch creates does not exist in the current tree. */ -static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st) +static int check_preimage(struct apply_state *state, + struct patch *patch, + struct cache_entry **ce, + struct stat *st) { const char *old_name = patch->old_name; struct patch *previous = NULL; @@ -3524,7 +3530,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s return error(_("%s: %s"), old_name, strerror(errno)); } - if (check_index && !previous) { + if (state->check_index && !previous) { int pos = cache_name_pos(old_name, strlen(old_name)); if (pos < 0) { if (patch->is_new < 0) @@ -3574,11 +3580,13 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s #define EXISTS_IN_INDEX 1 #define EXISTS_IN_WORKTREE 2 -static int check_to_create(const char *new_name, int ok_if_exists) +static int check_to_create(struct apply_state *state, + const char *new_name, + int ok_if_exists) { struct stat nst; - if (check_index && + if (state->check_index && cache_name_pos(new_name, strlen(new_name)) >= 0 && !ok_if_exists) return EXISTS_IN_INDEX; @@ -3654,7 +3662,7 @@ static void prepare_symlink_changes(struct patch *patch) } } -static int path_is_beyond_symlink_1(struct strbuf *name) +static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *name) { do { unsigned int change; @@ -3675,7 +3683,7 @@ static int path_is_beyond_symlink_1(struct strbuf *name) continue; /* otherwise, check the preimage */ - if (check_index) { + if (state->check_index) { struct cache_entry *ce; ce = cache_file_exists(name->buf, name->len, ignore_case); @@ -3690,14 +3698,14 @@ static int path_is_beyond_symlink_1(struct strbuf *name) return 0; } -static int path_is_beyond_symlink(const char *name_) +static int path_is_beyond_symlink(struct apply_state *state, const char *name_) { int ret; struct strbuf name = STRBUF_INIT; assert(*name_ != '\0'); strbuf_addstr(&name, name_); - ret = path_is_beyond_symlink_1(&name); + ret = path_is_beyond_symlink_1(state, &name); strbuf_release(&name); return ret; @@ -3737,7 +3745,7 @@ static int check_patch(struct apply_state *state, struct patch *patch) patch->rejected = 1; /* we will drop this after we succeed */ - status = check_preimage(patch, &ce, &st); + status = check_preimage(state, patch, &ce, &st); if (status) return status; old_name = patch->old_name; @@ -3764,7 +3772,7 @@ static int check_patch(struct apply_state *state, struct patch *patch) if (new_name && ((0 < patch->is_new) || patch->is_rename || patch->is_copy)) { - int err = check_to_create(new_name, ok_if_exists); + int err = check_to_create(state, new_name, ok_if_exists); if (err && threeway) { patch->direct_to_threeway = 1; @@ -3819,7 +3827,7 @@ static int check_patch(struct apply_state *state, struct patch *patch) * is not deposited to a path that is beyond a symbolic link * here. */ - if (!patch->is_delete && path_is_beyond_symlink(patch->new_name)) + if (!patch->is_delete && path_is_beyond_symlink(state, patch->new_name)) return error(_("affected file '%s' is beyond a symbolic link"), patch->new_name); @@ -4431,11 +4439,11 @@ static int apply_patch(struct apply_state *state, if (whitespace_error && (ws_error_action == die_on_ws_error)) apply = 0; - update_index = check_index && apply; + update_index = state->check_index && apply; if (update_index && newfd < 0) newfd = hold_locked_index(&lock_file, 1); - if (check_index) { + if (state->check_index) { if (read_cache() < 0) die(_("unable to read index file")); } @@ -4581,7 +4589,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) N_("instead of applying the patch, output a summary for the input")), OPT_BOOL(0, "check", &state.check, N_("instead of applying the patch, see if the patch is applicable")), - OPT_BOOL(0, "index", &check_index, + OPT_BOOL(0, "index", &state.check_index, N_("make sure the patch is applicable to the current index")), OPT_BOOL(0, "cached", &cached, N_("apply a patch without touching the working tree")), @@ -4640,20 +4648,20 @@ int cmd_apply(int argc, const char **argv, const char *prefix) if (threeway) { if (is_not_gitdir) die(_("--3way outside a repository")); - check_index = 1; + state.check_index = 1; } if (apply_with_reject) apply = apply_verbosely = 1; if (!force_apply && (diffstat || numstat || summary || state.check || fake_ancestor)) apply = 0; - if (check_index && is_not_gitdir) + if (state.check_index && is_not_gitdir) die(_("--index outside a repository")); if (cached) { if (is_not_gitdir) die(_("--cached outside a repository")); - check_index = 1; + state.check_index = 1; } - if (check_index) + if (state.check_index) unsafe_paths = 0; for (i = 0; i < argc; i++) { -- cgit v1.2.1 From 2595a8b146e86a3217cd6ee44bcc889cc2fe774f Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:50 +0200 Subject: builtin/apply: move 'apply_in_reverse' global into 'struct apply_state' To libify the apply functionality the 'apply_in_reverse' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 50 +++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 769383cd5d..796d990910 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -30,6 +30,7 @@ struct apply_state { int check_index; /* preimage must match the indexed version */ /* These boolean parameters control how the apply is done */ + int apply_in_reverse; int unidiff_zero; }; @@ -49,7 +50,6 @@ static int diffstat; static int numstat; static int summary; static int apply = 1; -static int apply_in_reverse; static int apply_with_reject; static int apply_verbosely; static int allow_overlap; @@ -1556,8 +1556,11 @@ static void check_whitespace(const char *line, int len, unsigned ws_rule) * between a "---" that is part of a patch, and a "---" that starts * the next patch is to look at the line counts.. */ -static int parse_fragment(const char *line, unsigned long size, - struct patch *patch, struct fragment *fragment) +static int parse_fragment(struct apply_state *state, + const char *line, + unsigned long size, + struct patch *patch, + struct fragment *fragment) { int added, deleted; int len = linelen(line, size), offset; @@ -1597,12 +1600,12 @@ static int parse_fragment(const char *line, unsigned long size, if (!deleted && !added) leading++; trailing++; - if (!apply_in_reverse && + if (!state->apply_in_reverse && ws_error_action == correct_ws_error) check_whitespace(line, len, patch->ws_rule); break; case '-': - if (apply_in_reverse && + if (state->apply_in_reverse && ws_error_action != nowarn_ws_error) check_whitespace(line, len, patch->ws_rule); deleted++; @@ -1610,7 +1613,7 @@ static int parse_fragment(const char *line, unsigned long size, trailing = 0; break; case '+': - if (!apply_in_reverse && + if (!state->apply_in_reverse && ws_error_action != nowarn_ws_error) check_whitespace(line, len, patch->ws_rule); added++; @@ -1666,7 +1669,10 @@ static int parse_fragment(const char *line, unsigned long size, * The (fragment->patch, fragment->size) pair points into the memory given * by the caller, not a copy, when we return. */ -static int parse_single_patch(const char *line, unsigned long size, struct patch *patch) +static int parse_single_patch(struct apply_state *state, + const char *line, + unsigned long size, + struct patch *patch) { unsigned long offset = 0; unsigned long oldlines = 0, newlines = 0, context = 0; @@ -1678,7 +1684,7 @@ static int parse_single_patch(const char *line, unsigned long size, struct patch fragment = xcalloc(1, sizeof(*fragment)); fragment->linenr = state_linenr; - len = parse_fragment(line, size, patch, fragment); + len = parse_fragment(state, line, size, patch, fragment); if (len <= 0) die(_("corrupt patch at line %d"), state_linenr); fragment->patch = line; @@ -2008,8 +2014,10 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si ? patch->new_name : patch->old_name); - patchsize = parse_single_patch(buffer + offset + hdrsize, - size - offset - hdrsize, patch); + patchsize = parse_single_patch(state, + buffer + offset + hdrsize, + size - offset - hdrsize, + patch); if (!patchsize) { static const char git_binary[] = "GIT binary patch\n"; @@ -2741,7 +2749,7 @@ static int apply_one_fragment(struct apply_state *state, if (len < size && patch[len] == '\\') plen--; first = *patch; - if (apply_in_reverse) { + if (state->apply_in_reverse) { if (first == '-') first = '+'; else if (first == '+') @@ -2914,7 +2922,7 @@ static int apply_one_fragment(struct apply_state *state, if (apply_verbosely && applied_pos != pos) { int offset = applied_pos - pos; - if (apply_in_reverse) + if (state->apply_in_reverse) offset = 0 - offset; fprintf_ln(stderr, Q_("Hunk #%d succeeded at %d (offset %d line).", @@ -2948,7 +2956,9 @@ out: return (applied_pos < 0); } -static int apply_binary_fragment(struct image *img, struct patch *patch) +static int apply_binary_fragment(struct apply_state *state, + struct image *img, + struct patch *patch) { struct fragment *fragment = patch->fragments; unsigned long len; @@ -2961,7 +2971,7 @@ static int apply_binary_fragment(struct image *img, struct patch *patch) patch->old_name); /* Binary patch is irreversible without the optional second hunk */ - if (apply_in_reverse) { + if (state->apply_in_reverse) { if (!fragment->next) return error("cannot reverse-apply a binary patch " "without the reverse hunk to '%s'", @@ -2994,7 +3004,9 @@ static int apply_binary_fragment(struct image *img, struct patch *patch) * but the preimage prepared by the caller in "img" is freed here * or in the helper function apply_binary_fragment() this calls. */ -static int apply_binary(struct image *img, struct patch *patch) +static int apply_binary(struct apply_state *state, + struct image *img, + struct patch *patch) { const char *name = patch->old_name ? patch->old_name : patch->new_name; unsigned char sha1[20]; @@ -3055,7 +3067,7 @@ static int apply_binary(struct image *img, struct patch *patch) * apply the patch data to it, which is stored * in the patch->fragments->{patch,size}. */ - if (apply_binary_fragment(img, patch)) + if (apply_binary_fragment(state, img, patch)) return error(_("binary patch does not apply to '%s'"), name); @@ -3078,7 +3090,7 @@ static int apply_fragments(struct apply_state *state, struct image *img, struct int nth = 0; if (patch->is_binary) - return apply_binary(img, patch); + return apply_binary(state, img, patch); while (frag) { nth++; @@ -4417,7 +4429,7 @@ static int apply_patch(struct apply_state *state, free_patch(patch); break; } - if (apply_in_reverse) + if (state->apply_in_reverse) reverse_patches(patch); if (use_patch(state, patch)) { patch_stats(patch); @@ -4615,7 +4627,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL, N_("ignore changes in whitespace when finding context"), PARSE_OPT_NOARG, option_parse_space_change }, - OPT_BOOL('R', "reverse", &apply_in_reverse, + OPT_BOOL('R', "reverse", &state.apply_in_reverse, N_("apply the patch in reverse")), OPT_BOOL(0, "unidiff-zero", &state.unidiff_zero, N_("don't expect at least one line of context")), -- cgit v1.2.1 From 30b5ae4d41c7e5b5655fb0e737db4764a17033c0 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:51 +0200 Subject: builtin/apply: move 'apply_with_reject' global into 'struct apply_state' To libify the apply functionality the 'apply_with_reject' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 796d990910..8692e2f7ef 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -31,6 +31,7 @@ struct apply_state { /* These boolean parameters control how the apply is done */ int apply_in_reverse; + int apply_with_reject; int unidiff_zero; }; @@ -50,7 +51,6 @@ static int diffstat; static int numstat; static int summary; static int apply = 1; -static int apply_with_reject; static int apply_verbosely; static int allow_overlap; static int no_add; @@ -3096,7 +3096,7 @@ static int apply_fragments(struct apply_state *state, struct image *img, struct nth++; if (apply_one_fragment(state, img, frag, inaccurate_eof, ws_rule, nth)) { error(_("patch failed: %s:%ld"), name, frag->oldpos); - if (!apply_with_reject) + if (!state->apply_with_reject) return -1; frag->rejected = 1; } @@ -4462,11 +4462,11 @@ static int apply_patch(struct apply_state *state, if ((state->check || apply) && check_patch_list(state, list) < 0 && - !apply_with_reject) + !state->apply_with_reject) exit(1); if (apply && write_out_results(list)) { - if (apply_with_reject) + if (state->apply_with_reject) exit(1); /* with --3way, we still need to write the index out */ return 1; @@ -4631,7 +4631,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) N_("apply the patch in reverse")), OPT_BOOL(0, "unidiff-zero", &state.unidiff_zero, N_("don't expect at least one line of context")), - OPT_BOOL(0, "reject", &apply_with_reject, + OPT_BOOL(0, "reject", &state.apply_with_reject, N_("leave the rejected hunks in corresponding *.rej files")), OPT_BOOL(0, "allow-overlap", &allow_overlap, N_("allow overlapping hunks")), @@ -4653,7 +4653,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, state.prefix, builtin_apply_options, apply_usage, 0); - if (apply_with_reject && threeway) + if (state.apply_with_reject && threeway) die("--reject and --3way cannot be used together."); if (cached && threeway) die("--cached and --3way cannot be used together."); @@ -4662,7 +4662,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) die(_("--3way outside a repository")); state.check_index = 1; } - if (apply_with_reject) + if (state.apply_with_reject) apply = apply_verbosely = 1; if (!force_apply && (diffstat || numstat || summary || state.check || fake_ancestor)) apply = 0; -- cgit v1.2.1 From 5cae882d27c99604bc654ea35102d17b178affdd Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:52 +0200 Subject: builtin/apply: move 'apply_verbosely' global into 'struct apply_state' To libify the apply functionality the 'apply_verbosely' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 8692e2f7ef..07dc89d153 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -32,6 +32,7 @@ struct apply_state { /* These boolean parameters control how the apply is done */ int apply_in_reverse; int apply_with_reject; + int apply_verbosely; int unidiff_zero; }; @@ -51,7 +52,6 @@ static int diffstat; static int numstat; static int summary; static int apply = 1; -static int apply_verbosely; static int allow_overlap; static int no_add; static int threeway; @@ -2805,7 +2805,7 @@ static int apply_one_fragment(struct apply_state *state, /* Ignore it, we already handled it */ break; default: - if (apply_verbosely) + if (state->apply_verbosely) error(_("invalid start of line: '%c'"), first); applied_pos = -1; goto out; @@ -2920,7 +2920,7 @@ static int apply_one_fragment(struct apply_state *state, apply = 0; } - if (apply_verbosely && applied_pos != pos) { + if (state->apply_verbosely && applied_pos != pos) { int offset = applied_pos - pos; if (state->apply_in_reverse) offset = 0 - offset; @@ -2942,7 +2942,7 @@ static int apply_one_fragment(struct apply_state *state, leading, trailing, applied_pos+1); update_image(img, applied_pos, &preimage, &postimage); } else { - if (apply_verbosely) + if (state->apply_verbosely) error(_("while searching for:\n%.*s"), (int)(old - oldlines), oldlines); } @@ -3856,7 +3856,7 @@ static int check_patch_list(struct apply_state *state, struct patch *patch) prepare_symlink_changes(patch); prepare_fn_table(patch); while (patch) { - if (apply_verbosely) + if (state->apply_verbosely) say_patch_name(stderr, _("Checking patch %s..."), patch); err |= check_patch(state, patch); @@ -4287,7 +4287,7 @@ static void write_out_one_result(struct patch *patch, int phase) create_file(patch); } -static int write_out_one_reject(struct patch *patch) +static int write_out_one_reject(struct apply_state *state, struct patch *patch) { FILE *rej; char namebuf[PATH_MAX]; @@ -4302,7 +4302,7 @@ static int write_out_one_reject(struct patch *patch) } if (!cnt) { - if (apply_verbosely) + if (state->apply_verbosely) say_patch_name(stderr, _("Applied patch %s cleanly."), patch); return 0; @@ -4358,7 +4358,7 @@ static int write_out_one_reject(struct patch *patch) return -1; } -static int write_out_results(struct patch *list) +static int write_out_results(struct apply_state *state, struct patch *list) { int phase; int errs = 0; @@ -4373,7 +4373,7 @@ static int write_out_results(struct patch *list) else { write_out_one_result(l, phase); if (phase == 1) { - if (write_out_one_reject(l)) + if (write_out_one_reject(state, l)) errs = 1; if (l->conflicted_threeway) { string_list_append(&cpath, l->new_name); @@ -4437,7 +4437,7 @@ static int apply_patch(struct apply_state *state, listp = &patch->next; } else { - if (apply_verbosely) + if (state->apply_verbosely) say_patch_name(stderr, _("Skipped patch '%s'."), patch); free_patch(patch); skipped_patch++; @@ -4465,7 +4465,7 @@ static int apply_patch(struct apply_state *state, !state->apply_with_reject) exit(1); - if (apply && write_out_results(list)) { + if (apply && write_out_results(state, list)) { if (state->apply_with_reject) exit(1); /* with --3way, we still need to write the index out */ @@ -4635,7 +4635,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) N_("leave the rejected hunks in corresponding *.rej files")), OPT_BOOL(0, "allow-overlap", &allow_overlap, N_("allow overlapping hunks")), - OPT__VERBOSE(&apply_verbosely, N_("be verbose")), + OPT__VERBOSE(&state.apply_verbosely, N_("be verbose")), OPT_BIT(0, "inaccurate-eof", &options, N_("tolerate incorrectly detected missing new-line at the end of file"), INACCURATE_EOF), @@ -4663,7 +4663,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) state.check_index = 1; } if (state.apply_with_reject) - apply = apply_verbosely = 1; + apply = state.apply_verbosely = 1; if (!force_apply && (diffstat || numstat || summary || state.check || fake_ancestor)) apply = 0; if (state.check_index && is_not_gitdir) -- cgit v1.2.1 From 901f9c6d42dd9857ecdac76413014c1497d34614 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:53 +0200 Subject: builtin/apply: move 'update_index' global into 'struct apply_state' To libify the apply functionality the 'update_index' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 07dc89d153..7b5abb936e 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -28,6 +28,7 @@ struct apply_state { /* These control what gets looked at and modified */ int check; /* preimage must match working tree, don't actually apply */ int check_index; /* preimage must match the indexed version */ + int update_index; /* check_index && apply */ /* These boolean parameters control how the apply is done */ int apply_in_reverse; @@ -46,7 +47,6 @@ static int newfd = -1; static int state_p_value = 1; static int p_value_known; -static int update_index; static int cached; static int diffstat; static int numstat; @@ -4090,9 +4090,9 @@ static void patch_stats(struct patch *patch) } } -static void remove_file(struct patch *patch, int rmdir_empty) +static void remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty) { - if (update_index) { + if (state->update_index) { if (remove_file_from_cache(patch->old_name) < 0) die(_("unable to remove %s from index"), patch->old_name); } @@ -4103,14 +4103,18 @@ static void remove_file(struct patch *patch, int rmdir_empty) } } -static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size) +static void add_index_file(struct apply_state *state, + const char *path, + unsigned mode, + void *buf, + unsigned long size) { struct stat st; struct cache_entry *ce; int namelen = strlen(path); unsigned ce_size = cache_entry_size(namelen); - if (!update_index) + if (!state->update_index) return; ce = xcalloc(1, ce_size); @@ -4220,13 +4224,14 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned die_errno(_("unable to write file '%s' mode %o"), path, mode); } -static void add_conflicted_stages_file(struct patch *patch) +static void add_conflicted_stages_file(struct apply_state *state, + struct patch *patch) { int stage, namelen; unsigned ce_size, mode; struct cache_entry *ce; - if (!update_index) + if (!state->update_index) return; namelen = strlen(patch->new_name); ce_size = cache_entry_size(namelen); @@ -4247,7 +4252,7 @@ static void add_conflicted_stages_file(struct patch *patch) } } -static void create_file(struct patch *patch) +static void create_file(struct apply_state *state, struct patch *patch) { char *path = patch->new_name; unsigned mode = patch->new_mode; @@ -4259,22 +4264,24 @@ static void create_file(struct patch *patch) create_one_file(path, mode, buf, size); if (patch->conflicted_threeway) - add_conflicted_stages_file(patch); + add_conflicted_stages_file(state, patch); else - add_index_file(path, mode, buf, size); + add_index_file(state, path, mode, buf, size); } /* phase zero is to remove, phase one is to create */ -static void write_out_one_result(struct patch *patch, int phase) +static void write_out_one_result(struct apply_state *state, + struct patch *patch, + int phase) { if (patch->is_delete > 0) { if (phase == 0) - remove_file(patch, 1); + remove_file(state, patch, 1); return; } if (patch->is_new > 0 || patch->is_copy) { if (phase == 1) - create_file(patch); + create_file(state, patch); return; } /* @@ -4282,9 +4289,9 @@ static void write_out_one_result(struct patch *patch, int phase) * thing: remove the old, write the new */ if (phase == 0) - remove_file(patch, patch->is_rename); + remove_file(state, patch, patch->is_rename); if (phase == 1) - create_file(patch); + create_file(state, patch); } static int write_out_one_reject(struct apply_state *state, struct patch *patch) @@ -4371,7 +4378,7 @@ static int write_out_results(struct apply_state *state, struct patch *list) if (l->rejected) errs = 1; else { - write_out_one_result(l, phase); + write_out_one_result(state, l, phase); if (phase == 1) { if (write_out_one_reject(state, l)) errs = 1; @@ -4451,8 +4458,8 @@ static int apply_patch(struct apply_state *state, if (whitespace_error && (ws_error_action == die_on_ws_error)) apply = 0; - update_index = state->check_index && apply; - if (update_index && newfd < 0) + state->update_index = state->check_index && apply; + if (state->update_index && newfd < 0) newfd = hold_locked_index(&lock_file, 1); if (state->check_index) { @@ -4727,7 +4734,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) whitespace_error); } - if (update_index) { + if (state.update_index) { if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) die(_("Unable to write new index file")); } -- cgit v1.2.1 From 6ca4c390933597f20db006445e844797ca7cd2c9 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:54 +0200 Subject: builtin/apply: move 'allow_overlap' global into 'struct apply_state' To libify the apply functionality the 'allow_overlap' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 7b5abb936e..ad3bec4431 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -31,6 +31,7 @@ struct apply_state { int update_index; /* check_index && apply */ /* These boolean parameters control how the apply is done */ + int allow_overlap; int apply_in_reverse; int apply_with_reject; int apply_verbosely; @@ -52,7 +53,6 @@ static int diffstat; static int numstat; static int summary; static int apply = 1; -static int allow_overlap; static int no_add; static int threeway; static int unsafe_paths; @@ -2627,7 +2627,8 @@ static void remove_last_line(struct image *img) * apply at applied_pos (counts in line numbers) in "img". * Update "img" to remove "preimage" and replace it with "postimage". */ -static void update_image(struct image *img, +static void update_image(struct apply_state *state, + struct image *img, int applied_pos, struct image *preimage, struct image *postimage) @@ -2692,7 +2693,7 @@ static void update_image(struct image *img, memcpy(img->line + applied_pos, postimage->line, postimage->nr * sizeof(*img->line)); - if (!allow_overlap) + if (!state->allow_overlap) for (i = 0; i < postimage->nr; i++) img->line[applied_pos + i].flag |= LINE_PATCHED; img->nr = nr; @@ -2940,7 +2941,7 @@ static int apply_one_fragment(struct apply_state *state, fprintf_ln(stderr, _("Context reduced to (%ld/%ld)" " to apply fragment at %d"), leading, trailing, applied_pos+1); - update_image(img, applied_pos, &preimage, &postimage); + update_image(state, img, applied_pos, &preimage, &postimage); } else { if (state->apply_verbosely) error(_("while searching for:\n%.*s"), @@ -4640,7 +4641,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) N_("don't expect at least one line of context")), OPT_BOOL(0, "reject", &state.apply_with_reject, N_("leave the rejected hunks in corresponding *.rej files")), - OPT_BOOL(0, "allow-overlap", &allow_overlap, + OPT_BOOL(0, "allow-overlap", &state.allow_overlap, N_("allow overlapping hunks")), OPT__VERBOSE(&state.apply_verbosely, N_("be verbose")), OPT_BIT(0, "inaccurate-eof", &options, -- cgit v1.2.1 From 885eefb12d35e416e637a92d8a6e7ac17eefd5cc Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:55 +0200 Subject: builtin/apply: move 'cached' global into 'struct apply_state' To libify the apply functionality the 'cached' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index ad3bec4431..9cba4606eb 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -26,6 +26,7 @@ struct apply_state { int prefix_length; /* These control what gets looked at and modified */ + int cached; /* apply to the index only */ int check; /* preimage must match working tree, don't actually apply */ int check_index; /* preimage must match the indexed version */ int update_index; /* check_index && apply */ @@ -42,13 +43,11 @@ struct apply_state { * --stat does just a diffstat, and doesn't actually apply * --numstat does numeric diffstat, and doesn't actually apply * --index-info shows the old and new index info for paths if available. - * --cached updates only the cache without ever touching the working tree. */ static int newfd = -1; static int state_p_value = 1; static int p_value_known; -static int cached; static int diffstat; static int numstat; static int summary; @@ -3264,7 +3263,7 @@ static int load_patch_target(struct apply_state *state, const char *name, unsigned expected_mode) { - if (cached || state->check_index) { + if (state->cached || state->check_index) { if (read_file_or_gitlink(ce, buf)) return error(_("read of %s failed"), name); } else if (name) { @@ -3537,7 +3536,7 @@ static int check_preimage(struct apply_state *state, return error(_("path %s has been renamed/deleted"), old_name); if (previous) { st_mode = previous->new_mode; - } else if (!cached) { + } else if (!state->cached) { stat_ret = lstat(old_name, st); if (stat_ret && errno != ENOENT) return error(_("%s: %s"), old_name, strerror(errno)); @@ -3555,9 +3554,9 @@ static int check_preimage(struct apply_state *state, if (checkout_target(&the_index, *ce, st)) return -1; } - if (!cached && verify_index_match(*ce, st)) + if (!state->cached && verify_index_match(*ce, st)) return error(_("%s: does not match index"), old_name); - if (cached) + if (state->cached) st_mode = (*ce)->ce_mode; } else if (stat_ret < 0) { if (patch->is_new < 0) @@ -3565,7 +3564,7 @@ static int check_preimage(struct apply_state *state, return error(_("%s: %s"), old_name, strerror(errno)); } - if (!cached && !previous) + if (!state->cached && !previous) st_mode = ce_mode_from_stat(*ce, st->st_mode); if (patch->is_new < 0) @@ -3603,7 +3602,7 @@ static int check_to_create(struct apply_state *state, cache_name_pos(new_name, strlen(new_name)) >= 0 && !ok_if_exists) return EXISTS_IN_INDEX; - if (cached) + if (state->cached) return 0; if (!lstat(new_name, &nst)) { @@ -4097,7 +4096,7 @@ static void remove_file(struct apply_state *state, struct patch *patch, int rmdi if (remove_file_from_cache(patch->old_name) < 0) die(_("unable to remove %s from index"), patch->old_name); } - if (!cached) { + if (!state->cached) { if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) { remove_path(patch->old_name); } @@ -4130,7 +4129,7 @@ static void add_index_file(struct apply_state *state, get_sha1_hex(s, ce->sha1)) die(_("corrupt patch for submodule %s"), path); } else { - if (!cached) { + if (!state->cached) { if (lstat(path, &st) < 0) die_errno(_("unable to stat newly created file '%s'"), path); @@ -4182,9 +4181,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf, * which is true 99% of the time anyway. If they don't, * we create them and try again. */ -static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size) +static void create_one_file(struct apply_state *state, + char *path, + unsigned mode, + const char *buf, + unsigned long size) { - if (cached) + if (state->cached) return; if (!try_create_file(path, mode, buf, size)) return; @@ -4262,7 +4265,7 @@ static void create_file(struct apply_state *state, struct patch *patch) if (!mode) mode = S_IFREG | 0644; - create_one_file(path, mode, buf, size); + create_one_file(state, path, mode, buf, size); if (patch->conflicted_threeway) add_conflicted_stages_file(state, patch); @@ -4611,7 +4614,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) N_("instead of applying the patch, see if the patch is applicable")), OPT_BOOL(0, "index", &state.check_index, N_("make sure the patch is applicable to the current index")), - OPT_BOOL(0, "cached", &cached, + OPT_BOOL(0, "cached", &state.cached, N_("apply a patch without touching the working tree")), OPT_BOOL(0, "unsafe-paths", &unsafe_paths, N_("accept a patch that touches outside the working area")), @@ -4663,7 +4666,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) if (state.apply_with_reject && threeway) die("--reject and --3way cannot be used together."); - if (cached && threeway) + if (state.cached && threeway) die("--cached and --3way cannot be used together."); if (threeway) { if (is_not_gitdir) @@ -4676,7 +4679,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) apply = 0; if (state.check_index && is_not_gitdir) die(_("--index outside a repository")); - if (cached) { + if (state.cached) { if (is_not_gitdir) die(_("--cached outside a repository")); state.check_index = 1; -- cgit v1.2.1 From c4f5c39862671e7de1ffca959220ef94385b1972 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:56 +0200 Subject: builtin/apply: move 'diffstat' global into 'struct apply_state' To libify the apply functionality the 'diffstat' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 9cba4606eb..d940125ee2 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -31,6 +31,9 @@ struct apply_state { int check_index; /* preimage must match the indexed version */ int update_index; /* check_index && apply */ + /* These control cosmetic aspect of the output */ + int diffstat; /* just show a diffstat, and don't actually apply */ + /* These boolean parameters control how the apply is done */ int allow_overlap; int apply_in_reverse; @@ -40,7 +43,6 @@ struct apply_state { }; /* - * --stat does just a diffstat, and doesn't actually apply * --numstat does numeric diffstat, and doesn't actually apply * --index-info shows the old and new index info for paths if available. */ @@ -48,7 +50,6 @@ static int newfd = -1; static int state_p_value = 1; static int p_value_known; -static int diffstat; static int numstat; static int summary; static int apply = 1; @@ -4486,7 +4487,7 @@ static int apply_patch(struct apply_state *state, if (fake_ancestor) build_fake_ancestor(list, fake_ancestor); - if (diffstat) + if (state->diffstat) stat_patch_list(list); if (numstat) @@ -4602,7 +4603,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) 0, option_parse_p }, OPT_BOOL(0, "no-add", &no_add, N_("ignore additions made by the patch")), - OPT_BOOL(0, "stat", &diffstat, + OPT_BOOL(0, "stat", &state.diffstat, N_("instead of applying the patch, output diffstat for the input")), OPT_NOOP_NOARG(0, "allow-binary-replacement"), OPT_NOOP_NOARG(0, "binary"), @@ -4675,7 +4676,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) } if (state.apply_with_reject) apply = state.apply_verbosely = 1; - if (!force_apply && (diffstat || numstat || summary || state.check || fake_ancestor)) + if (!force_apply && (state.diffstat || numstat || summary || state.check || fake_ancestor)) apply = 0; if (state.check_index && is_not_gitdir) die(_("--index outside a repository")); -- cgit v1.2.1 From 179070b91c0ae1baf889f083596398a4f72286ea Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:57 +0200 Subject: builtin/apply: move 'numstat' global into 'struct apply_state' To libify the apply functionality the 'numstat' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index d940125ee2..47a45a71d5 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -33,6 +33,7 @@ struct apply_state { /* These control cosmetic aspect of the output */ int diffstat; /* just show a diffstat, and don't actually apply */ + int numstat; /* just show a numeric diffstat, and don't actually apply */ /* These boolean parameters control how the apply is done */ int allow_overlap; @@ -43,14 +44,12 @@ struct apply_state { }; /* - * --numstat does numeric diffstat, and doesn't actually apply * --index-info shows the old and new index info for paths if available. */ static int newfd = -1; static int state_p_value = 1; static int p_value_known; -static int numstat; static int summary; static int apply = 1; static int no_add; @@ -4490,7 +4489,7 @@ static int apply_patch(struct apply_state *state, if (state->diffstat) stat_patch_list(list); - if (numstat) + if (state->numstat) numstat_patch_list(list); if (summary) @@ -4607,7 +4606,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) N_("instead of applying the patch, output diffstat for the input")), OPT_NOOP_NOARG(0, "allow-binary-replacement"), OPT_NOOP_NOARG(0, "binary"), - OPT_BOOL(0, "numstat", &numstat, + OPT_BOOL(0, "numstat", &state.numstat, N_("show number of added and deleted lines in decimal notation")), OPT_BOOL(0, "summary", &summary, N_("instead of applying the patch, output a summary for the input")), @@ -4676,7 +4675,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) } if (state.apply_with_reject) apply = state.apply_verbosely = 1; - if (!force_apply && (state.diffstat || numstat || summary || state.check || fake_ancestor)) + if (!force_apply && (state.diffstat || state.numstat || summary || state.check || fake_ancestor)) apply = 0; if (state.check_index && is_not_gitdir) die(_("--index outside a repository")); -- cgit v1.2.1 From 79a3efda795a88403cea3e9ff321b4b668f393d9 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:58 +0200 Subject: builtin/apply: move 'summary' global into 'struct apply_state' To libify the apply functionality the 'summary' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 47a45a71d5..f174a42a55 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -34,6 +34,7 @@ struct apply_state { /* These control cosmetic aspect of the output */ int diffstat; /* just show a diffstat, and don't actually apply */ int numstat; /* just show a numeric diffstat, and don't actually apply */ + int summary; /* just report creation, deletion, etc, and don't actually apply */ /* These boolean parameters control how the apply is done */ int allow_overlap; @@ -50,7 +51,6 @@ static int newfd = -1; static int state_p_value = 1; static int p_value_known; -static int summary; static int apply = 1; static int no_add; static int threeway; @@ -4492,7 +4492,7 @@ static int apply_patch(struct apply_state *state, if (state->numstat) numstat_patch_list(list); - if (summary) + if (state->summary) summary_patch_list(list); free_patch_list(list); @@ -4608,7 +4608,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) OPT_NOOP_NOARG(0, "binary"), OPT_BOOL(0, "numstat", &state.numstat, N_("show number of added and deleted lines in decimal notation")), - OPT_BOOL(0, "summary", &summary, + OPT_BOOL(0, "summary", &state.summary, N_("instead of applying the patch, output a summary for the input")), OPT_BOOL(0, "check", &state.check, N_("instead of applying the patch, see if the patch is applicable")), @@ -4675,7 +4675,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) } if (state.apply_with_reject) apply = state.apply_verbosely = 1; - if (!force_apply && (state.diffstat || state.numstat || summary || state.check || fake_ancestor)) + if (!force_apply && (state.diffstat || state.numstat || state.summary || state.check || fake_ancestor)) apply = 0; if (state.check_index && is_not_gitdir) die(_("--index outside a repository")); -- cgit v1.2.1 From b12e888f7a9489b964ad266b27d7fcfa62fa78e4 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:10:59 +0200 Subject: builtin/apply: move 'threeway' global into 'struct apply_state' To libify the apply functionality the 'threeway' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index f174a42a55..d00017baf1 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -41,6 +41,7 @@ struct apply_state { int apply_in_reverse; int apply_with_reject; int apply_verbosely; + int threeway; int unidiff_zero; }; @@ -53,7 +54,6 @@ static int state_p_value = 1; static int p_value_known; static int apply = 1; static int no_add; -static int threeway; static int unsafe_paths; static const char *fake_ancestor; static int line_termination = '\n'; @@ -3491,7 +3491,7 @@ static int apply_data(struct apply_state *state, struct patch *patch, if (patch->direct_to_threeway || apply_fragments(state, &image, patch) < 0) { /* Note: with --reject, apply_fragments() returns 0 */ - if (!threeway || try_threeway(state, &image, patch, st, ce) < 0) + if (!state->threeway || try_threeway(state, &image, patch, st, ce) < 0) return -1; } patch->result = image.buf; @@ -3786,7 +3786,7 @@ static int check_patch(struct apply_state *state, struct patch *patch) ((0 < patch->is_new) || patch->is_rename || patch->is_copy)) { int err = check_to_create(state, new_name, ok_if_exists); - if (err && threeway) { + if (err && state->threeway) { patch->direct_to_threeway = 1; } else switch (err) { case 0: @@ -4620,7 +4620,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) N_("accept a patch that touches outside the working area")), OPT_BOOL(0, "apply", &force_apply, N_("also apply the patch (use with --stat/--summary/--check)")), - OPT_BOOL('3', "3way", &threeway, + OPT_BOOL('3', "3way", &state.threeway, N_( "attempt three-way merge if a patch does not apply")), OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor, N_("build a temporary index based on embedded index information")), @@ -4664,11 +4664,11 @@ int cmd_apply(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, state.prefix, builtin_apply_options, apply_usage, 0); - if (state.apply_with_reject && threeway) + if (state.apply_with_reject && state.threeway) die("--reject and --3way cannot be used together."); - if (state.cached && threeway) + if (state.cached && state.threeway) die("--cached and --3way cannot be used together."); - if (threeway) { + if (state.threeway) { if (is_not_gitdir) die(_("--3way outside a repository")); state.check_index = 1; -- cgit v1.2.1 From 1ff36a107f609978dedd8c5b56aad8ca6276f975 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:00 +0200 Subject: builtin/apply: move 'no_add' global into 'struct apply_state' To libify the apply functionality the 'no_add' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index d00017baf1..604e7bf8ff 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -41,6 +41,7 @@ struct apply_state { int apply_in_reverse; int apply_with_reject; int apply_verbosely; + int no_add; int threeway; int unidiff_zero; }; @@ -53,7 +54,6 @@ static int newfd = -1; static int state_p_value = 1; static int p_value_known; static int apply = 1; -static int no_add; static int unsafe_paths; static const char *fake_ancestor; static int line_termination = '\n'; @@ -2782,7 +2782,7 @@ static int apply_one_fragment(struct apply_state *state, /* Fall-through for ' ' */ case '+': /* --no-add does not add new lines */ - if (first == '+' && no_add) + if (first == '+' && state->no_add) break; start = newlines.len; @@ -4600,7 +4600,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"), N_("remove leading slashes from traditional diff paths"), 0, option_parse_p }, - OPT_BOOL(0, "no-add", &no_add, + OPT_BOOL(0, "no-add", &state.no_add, N_("ignore additions made by the patch")), OPT_BOOL(0, "stat", &state.diffstat, N_("instead of applying the patch, output diffstat for the input")), -- cgit v1.2.1 From 6c0c2bf56c7a3484d157661fb4660f5aefe67a83 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:01 +0200 Subject: builtin/apply: move 'unsafe_paths' global into 'struct apply_state' To libify the apply functionality the 'unsafe_paths' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 604e7bf8ff..4ef83c1be5 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -44,6 +44,7 @@ struct apply_state { int no_add; int threeway; int unidiff_zero; + int unsafe_paths; }; /* @@ -54,7 +55,6 @@ static int newfd = -1; static int state_p_value = 1; static int p_value_known; static int apply = 1; -static int unsafe_paths; static const char *fake_ancestor; static int line_termination = '\n'; static unsigned int p_context = UINT_MAX; @@ -3827,7 +3827,7 @@ static int check_patch(struct apply_state *state, struct patch *patch) } } - if (!unsafe_paths) + if (!state->unsafe_paths) die_on_unsafe_path(patch); /* @@ -4616,7 +4616,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) N_("make sure the patch is applicable to the current index")), OPT_BOOL(0, "cached", &state.cached, N_("apply a patch without touching the working tree")), - OPT_BOOL(0, "unsafe-paths", &unsafe_paths, + OPT_BOOL(0, "unsafe-paths", &state.unsafe_paths, N_("accept a patch that touches outside the working area")), OPT_BOOL(0, "apply", &force_apply, N_("also apply the patch (use with --stat/--summary/--check)")), @@ -4685,7 +4685,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) state.check_index = 1; } if (state.check_index) - unsafe_paths = 0; + state.unsafe_paths = 0; for (i = 0; i < argc; i++) { const char *arg = argv[i]; -- cgit v1.2.1 From f4c9eaa49c5ed87b304a40b95b1d06d46a8cbfa9 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:02 +0200 Subject: builtin/apply: move 'line_termination' global into 'struct apply_state' To libify the apply functionality the 'line_termination' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 4ef83c1be5..95cd60a3ee 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -45,6 +45,9 @@ struct apply_state { int threeway; int unidiff_zero; int unsafe_paths; + + /* Other non boolean parameters */ + int line_termination; }; /* @@ -56,7 +59,6 @@ static int state_p_value = 1; static int p_value_known; static int apply = 1; static const char *fake_ancestor; -static int line_termination = '\n'; static unsigned int p_context = UINT_MAX; static const char * const apply_usage[] = { N_("git apply [] [...]"), @@ -3977,7 +3979,8 @@ static void stat_patch_list(struct patch *patch) print_stat_summary(stdout, files, adds, dels); } -static void numstat_patch_list(struct patch *patch) +static void numstat_patch_list(struct apply_state *state, + struct patch *patch) { for ( ; patch; patch = patch->next) { const char *name; @@ -3986,7 +3989,7 @@ static void numstat_patch_list(struct patch *patch) printf("-\t-\t"); else printf("%d\t%d\t", patch->lines_added, patch->lines_deleted); - write_name_quoted(name, stdout, line_termination); + write_name_quoted(name, stdout, state->line_termination); } } @@ -4490,7 +4493,7 @@ static int apply_patch(struct apply_state *state, stat_patch_list(list); if (state->numstat) - numstat_patch_list(list); + numstat_patch_list(state, list); if (state->summary) summary_patch_list(list); @@ -4565,6 +4568,7 @@ static void init_apply_state(struct apply_state *state, const char *prefix) memset(state, 0, sizeof(*state)); state->prefix = prefix; state->prefix_length = state->prefix ? strlen(state->prefix) : 0; + state->line_termination = '\n'; git_apply_config(); if (apply_default_whitespace) @@ -4625,7 +4629,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor, N_("build a temporary index based on embedded index information")), /* Think twice before adding "--nul" synonym to this */ - OPT_SET_INT('z', NULL, &line_termination, + OPT_SET_INT('z', NULL, &state.line_termination, N_("paths are separated with NUL character"), '\0'), OPT_INTEGER('C', NULL, &p_context, N_("ensure at least lines of context match")), -- cgit v1.2.1 From a0bfaf0796e416bb1cecd03e30af3e189a039a29 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:03 +0200 Subject: builtin/apply: move 'fake_ancestor' global into 'struct apply_state' To libify the apply functionality the 'fake_ancestor' variable should not be static and global to the file. Let's move it into 'struct apply_state'. By the way remove a comment about '--index-info' that was renamed '--build-fake-ancestor' in commit 26b28007689d27a921ea90e5a29fc8eb74b0d297 (apply: get rid of --index-info in favor of --build-fake-ancestor, Sep 17 2007). Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 95cd60a3ee..59b0f1b00a 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -47,18 +47,15 @@ struct apply_state { int unsafe_paths; /* Other non boolean parameters */ + const char *fake_ancestor; int line_termination; }; -/* - * --index-info shows the old and new index info for paths if available. - */ static int newfd = -1; static int state_p_value = 1; static int p_value_known; static int apply = 1; -static const char *fake_ancestor; static unsigned int p_context = UINT_MAX; static const char * const apply_usage[] = { N_("git apply [] [...]"), @@ -4486,8 +4483,8 @@ static int apply_patch(struct apply_state *state, return 1; } - if (fake_ancestor) - build_fake_ancestor(list, fake_ancestor); + if (state->fake_ancestor) + build_fake_ancestor(list, state->fake_ancestor); if (state->diffstat) stat_patch_list(list); @@ -4626,7 +4623,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) N_("also apply the patch (use with --stat/--summary/--check)")), OPT_BOOL('3', "3way", &state.threeway, N_( "attempt three-way merge if a patch does not apply")), - OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor, + OPT_FILENAME(0, "build-fake-ancestor", &state.fake_ancestor, N_("build a temporary index based on embedded index information")), /* Think twice before adding "--nul" synonym to this */ OPT_SET_INT('z', NULL, &state.line_termination, @@ -4679,7 +4676,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) } if (state.apply_with_reject) apply = state.apply_verbosely = 1; - if (!force_apply && (state.diffstat || state.numstat || state.summary || state.check || fake_ancestor)) + if (!force_apply && (state.diffstat || state.numstat || state.summary || state.check || state.fake_ancestor)) apply = 0; if (state.check_index && is_not_gitdir) die(_("--index outside a repository")); -- cgit v1.2.1 From a48f9bb1b31329b29eed9808844c147f613b59ad Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:04 +0200 Subject: builtin/apply: move 'p_context' global into 'struct apply_state' To libify the apply functionality the 'p_context' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 59b0f1b00a..3c9f05262b 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -49,6 +49,7 @@ struct apply_state { /* Other non boolean parameters */ const char *fake_ancestor; int line_termination; + unsigned int p_context; }; static int newfd = -1; @@ -56,7 +57,6 @@ static int newfd = -1; static int state_p_value = 1; static int p_value_known; static int apply = 1; -static unsigned int p_context = UINT_MAX; static const char * const apply_usage[] = { N_("git apply [] [...]"), NULL @@ -2872,7 +2872,7 @@ static int apply_one_fragment(struct apply_state *state, break; /* Am I at my context limits? */ - if ((leading <= p_context) && (trailing <= p_context)) + if ((leading <= state->p_context) && (trailing <= state->p_context)) break; if (match_beginning || match_end) { match_beginning = match_end = 0; @@ -4566,6 +4566,7 @@ static void init_apply_state(struct apply_state *state, const char *prefix) state->prefix = prefix; state->prefix_length = state->prefix ? strlen(state->prefix) : 0; state->line_termination = '\n'; + state->p_context = UINT_MAX; git_apply_config(); if (apply_default_whitespace) @@ -4628,7 +4629,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) /* Think twice before adding "--nul" synonym to this */ OPT_SET_INT('z', NULL, &state.line_termination, N_("paths are separated with NUL character"), '\0'), - OPT_INTEGER('C', NULL, &p_context, + OPT_INTEGER('C', NULL, &state.p_context, N_("ensure at least lines of context match")), { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"), N_("detect new or modified lines that have whitespace errors"), -- cgit v1.2.1 From 574f5a59d85153309ea9856f425ca610074e1116 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:05 +0200 Subject: builtin/apply: move 'apply' global into 'struct apply_state' To libify the apply functionality the 'apply' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 3c9f05262b..c0c18ce9fd 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -26,6 +26,7 @@ struct apply_state { int prefix_length; /* These control what gets looked at and modified */ + int apply; /* this is not a dry-run */ int cached; /* apply to the index only */ int check; /* preimage must match working tree, don't actually apply */ int check_index; /* preimage must match the indexed version */ @@ -56,7 +57,7 @@ static int newfd = -1; static int state_p_value = 1; static int p_value_known; -static int apply = 1; + static const char * const apply_usage[] = { N_("git apply [] [...]"), NULL @@ -126,10 +127,11 @@ static void parse_ignorewhitespace_option(const char *option) die(_("unrecognized whitespace ignore option '%s'"), option); } -static void set_default_whitespace_mode(const char *whitespace_option) +static void set_default_whitespace_mode(struct apply_state *state, + const char *whitespace_option) { if (!whitespace_option && !apply_default_whitespace) - ws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error); + ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error); } /* @@ -2058,7 +2060,7 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si * without metadata change. A binary patch appears * empty to us here. */ - if ((apply || state->check) && + if ((state->apply || state->check) && (!patch->is_binary && !metadata_changes(patch))) die(_("patch with only garbage at line %d"), state_linenr); } @@ -2916,7 +2918,7 @@ static int apply_one_fragment(struct apply_state *state, * apply_data->apply_fragments->apply_one_fragment */ if (ws_error_action == die_on_ws_error) - apply = 0; + state->apply = 0; } if (state->apply_verbosely && applied_pos != pos) { @@ -4460,9 +4462,9 @@ static int apply_patch(struct apply_state *state, die(_("unrecognized input")); if (whitespace_error && (ws_error_action == die_on_ws_error)) - apply = 0; + state->apply = 0; - state->update_index = state->check_index && apply; + state->update_index = state->check_index && state->apply; if (state->update_index && newfd < 0) newfd = hold_locked_index(&lock_file, 1); @@ -4471,12 +4473,12 @@ static int apply_patch(struct apply_state *state, die(_("unable to read index file")); } - if ((state->check || apply) && + if ((state->check || state->apply) && check_patch_list(state, list) < 0 && !state->apply_with_reject) exit(1); - if (apply && write_out_results(state, list)) { + if (state->apply && write_out_results(state, list)) { if (state->apply_with_reject) exit(1); /* with --3way, we still need to write the index out */ @@ -4565,6 +4567,7 @@ static void init_apply_state(struct apply_state *state, const char *prefix) memset(state, 0, sizeof(*state)); state->prefix = prefix; state->prefix_length = state->prefix ? strlen(state->prefix) : 0; + state->apply = 1; state->line_termination = '\n'; state->p_context = UINT_MAX; @@ -4676,9 +4679,9 @@ int cmd_apply(int argc, const char **argv, const char *prefix) state.check_index = 1; } if (state.apply_with_reject) - apply = state.apply_verbosely = 1; + state.apply = state.apply_verbosely = 1; if (!force_apply && (state.diffstat || state.numstat || state.summary || state.check || state.fake_ancestor)) - apply = 0; + state.apply = 0; if (state.check_index && is_not_gitdir) die(_("--index outside a repository")); if (state.cached) { @@ -4706,11 +4709,11 @@ int cmd_apply(int argc, const char **argv, const char *prefix) if (fd < 0) die_errno(_("can't open patch '%s'"), arg); read_stdin = 0; - set_default_whitespace_mode(whitespace_option); + set_default_whitespace_mode(&state, whitespace_option); errs |= apply_patch(&state, fd, arg, options); close(fd); } - set_default_whitespace_mode(whitespace_option); + set_default_whitespace_mode(&state, whitespace_option); if (read_stdin) errs |= apply_patch(&state, 0, "", options); if (whitespace_error) { @@ -4728,7 +4731,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) "%d lines add whitespace errors.", whitespace_error), whitespace_error); - if (applied_after_fixing_ws && apply) + if (applied_after_fixing_ws && state.apply) warning("%d line%s applied after" " fixing whitespace errors.", applied_after_fixing_ws, -- cgit v1.2.1 From b802355863814b331ffc4ae029b228472e874d1f Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:06 +0200 Subject: builtin/apply: move 'patch_input_file' global into 'struct apply_state' To libify the apply functionality the 'patch_input_file' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index c0c18ce9fd..fa9002898c 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -49,6 +49,7 @@ struct apply_state { /* Other non boolean parameters */ const char *fake_ancestor; + const char *patch_input_file; int line_termination; unsigned int p_context; }; @@ -79,7 +80,6 @@ static enum ws_ignore { } ws_ignore_action = ignore_ws_none; -static const char *patch_input_file; static struct strbuf root = STRBUF_INIT; static void parse_whitespace_option(const char *option) @@ -1525,7 +1525,11 @@ static int find_header(struct apply_state *state, return -1; } -static void record_ws_error(unsigned result, const char *line, int len, int linenr) +static void record_ws_error(struct apply_state *state, + unsigned result, + const char *line, + int len, + int linenr) { char *err; @@ -1539,15 +1543,18 @@ static void record_ws_error(unsigned result, const char *line, int len, int line err = whitespace_error_string(result); fprintf(stderr, "%s:%d: %s.\n%.*s\n", - patch_input_file, linenr, err, len, line); + state->patch_input_file, linenr, err, len, line); free(err); } -static void check_whitespace(const char *line, int len, unsigned ws_rule) +static void check_whitespace(struct apply_state *state, + const char *line, + int len, + unsigned ws_rule) { unsigned result = ws_check(line + 1, len - 1, ws_rule); - record_ws_error(result, line + 1, len - 2, state_linenr); + record_ws_error(state, result, line + 1, len - 2, state_linenr); } /* @@ -1602,12 +1609,12 @@ static int parse_fragment(struct apply_state *state, trailing++; if (!state->apply_in_reverse && ws_error_action == correct_ws_error) - check_whitespace(line, len, patch->ws_rule); + check_whitespace(state, line, len, patch->ws_rule); break; case '-': if (state->apply_in_reverse && ws_error_action != nowarn_ws_error) - check_whitespace(line, len, patch->ws_rule); + check_whitespace(state, line, len, patch->ws_rule); deleted++; oldlines--; trailing = 0; @@ -1615,7 +1622,7 @@ static int parse_fragment(struct apply_state *state, case '+': if (!state->apply_in_reverse && ws_error_action != nowarn_ws_error) - check_whitespace(line, len, patch->ws_rule); + check_whitespace(state, line, len, patch->ws_rule); added++; newlines--; trailing = 0; @@ -2904,7 +2911,7 @@ static int apply_one_fragment(struct apply_state *state, preimage.nr + applied_pos >= img->nr && (ws_rule & WS_BLANK_AT_EOF) && ws_error_action != nowarn_ws_error) { - record_ws_error(WS_BLANK_AT_EOF, "+", 1, + record_ws_error(state, WS_BLANK_AT_EOF, "+", 1, found_new_blank_lines_at_end); if (ws_error_action == correct_ws_error) { while (new_blank_lines_at_end--) @@ -4427,7 +4434,7 @@ static int apply_patch(struct apply_state *state, struct patch *list = NULL, **listp = &list; int skipped_patch = 0; - patch_input_file = filename; + state->patch_input_file = filename; read_patch_file(&buf, fd); offset = 0; while (offset < buf.len) { -- cgit v1.2.1 From 82f0dfca542e95b74ff967f10fb4247e11a2d9d9 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:07 +0200 Subject: builtin/apply: move 'limit_by_name' global into 'struct apply_state' To libify the apply functionality the 'limit_by_name' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index fa9002898c..0e3de17d05 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -52,6 +52,9 @@ struct apply_state { const char *patch_input_file; int line_termination; unsigned int p_context; + + /* Exclude and include path parameters */ + struct string_list limit_by_name; }; static int newfd = -1; @@ -1958,13 +1961,14 @@ static void prefix_patch(struct apply_state *state, struct patch *p) * include/exclude */ -static struct string_list limit_by_name; static int has_include; -static void add_name_limit(const char *name, int exclude) +static void add_name_limit(struct apply_state *state, + const char *name, + int exclude) { struct string_list_item *it; - it = string_list_append(&limit_by_name, name); + it = string_list_append(&state->limit_by_name, name); it->util = exclude ? NULL : (void *) 1; } @@ -1982,8 +1986,8 @@ static int use_patch(struct apply_state *state, struct patch *p) } /* See if it matches any of exclude/include rule */ - for (i = 0; i < limit_by_name.nr; i++) { - struct string_list_item *it = &limit_by_name.items[i]; + for (i = 0; i < state->limit_by_name.nr; i++) { + struct string_list_item *it = &state->limit_by_name.items[i]; if (!wildmatch(it->string, pathname, 0, NULL)) return (it->util != NULL); } @@ -4520,14 +4524,16 @@ static void git_apply_config(void) static int option_parse_exclude(const struct option *opt, const char *arg, int unset) { - add_name_limit(arg, 1); + struct apply_state *state = opt->value; + add_name_limit(state, arg, 1); return 0; } static int option_parse_include(const struct option *opt, const char *arg, int unset) { - add_name_limit(arg, 0); + struct apply_state *state = opt->value; + add_name_limit(state, arg, 0); has_include = 1; return 0; } @@ -4587,7 +4593,7 @@ static void init_apply_state(struct apply_state *state, const char *prefix) static void clear_apply_state(struct apply_state *state) { - /* empty for now */ + string_list_clear(&state->limit_by_name, 0); } int cmd_apply(int argc, const char **argv, const char *prefix) @@ -4603,10 +4609,10 @@ int cmd_apply(int argc, const char **argv, const char *prefix) const char *whitespace_option = NULL; struct option builtin_apply_options[] = { - { OPTION_CALLBACK, 0, "exclude", NULL, N_("path"), + { OPTION_CALLBACK, 0, "exclude", &state, N_("path"), N_("don't apply changes matching the given path"), 0, option_parse_exclude }, - { OPTION_CALLBACK, 0, "include", NULL, N_("path"), + { OPTION_CALLBACK, 0, "include", &state, N_("path"), N_("apply changes matching the given path"), 0, option_parse_include }, { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"), -- cgit v1.2.1 From 0c1138cbdbf7a82f8373077b77a1878f742a5222 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:08 +0200 Subject: builtin/apply: move 'has_include' global into 'struct apply_state' To libify the apply functionality the 'has_include' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 0e3de17d05..ebbc7111f8 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -55,6 +55,7 @@ struct apply_state { /* Exclude and include path parameters */ struct string_list limit_by_name; + int has_include; }; static int newfd = -1; @@ -1961,7 +1962,6 @@ static void prefix_patch(struct apply_state *state, struct patch *p) * include/exclude */ -static int has_include; static void add_name_limit(struct apply_state *state, const char *name, int exclude) @@ -1997,7 +1997,7 @@ static int use_patch(struct apply_state *state, struct patch *p) * not used. Otherwise, we saw bunch of exclude rules (or none) * and such a path is used. */ - return !has_include; + return !state->has_include; } @@ -4534,7 +4534,7 @@ static int option_parse_include(const struct option *opt, { struct apply_state *state = opt->value; add_name_limit(state, arg, 0); - has_include = 1; + state->has_include = 1; return 0; } -- cgit v1.2.1 From dbd23433e7b3d95bb03b44e9da86f713f1e35e17 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:09 +0200 Subject: builtin/apply: move 'p_value' global into 'struct apply_state' To libify the apply functionality the 'p_value' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 151 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 99 insertions(+), 52 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index ebbc7111f8..843fafd629 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -51,6 +51,7 @@ struct apply_state { const char *fake_ancestor; const char *patch_input_file; int line_termination; + int p_value; unsigned int p_context; /* Exclude and include path parameters */ @@ -60,7 +61,6 @@ struct apply_state { static int newfd = -1; -static int state_p_value = 1; static int p_value_known; static const char * const apply_usage[] = { @@ -881,24 +881,24 @@ static void parse_traditional_patch(struct apply_state *state, q = guess_p_value(state, second); if (p < 0) p = q; if (0 <= p && p == q) { - state_p_value = p; + state->p_value = p; p_value_known = 1; } } if (is_dev_null(first)) { patch->is_new = 1; patch->is_delete = 0; - name = find_name_traditional(second, NULL, state_p_value); + name = find_name_traditional(second, NULL, state->p_value); patch->new_name = name; } else if (is_dev_null(second)) { patch->is_new = 0; patch->is_delete = 1; - name = find_name_traditional(first, NULL, state_p_value); + name = find_name_traditional(first, NULL, state->p_value); patch->old_name = name; } else { char *first_name; - first_name = find_name_traditional(first, NULL, state_p_value); - name = find_name_traditional(second, first_name, state_p_value); + first_name = find_name_traditional(first, NULL, state->p_value); + name = find_name_traditional(second, first_name, state->p_value); free(first_name); if (has_epoch_timestamp(first)) { patch->is_new = 1; @@ -917,7 +917,9 @@ static void parse_traditional_patch(struct apply_state *state, die(_("unable to find filename in patch at line %d"), state_linenr); } -static int gitdiff_hdrend(const char *line, struct patch *patch) +static int gitdiff_hdrend(struct apply_state *state, + const char *line, + struct patch *patch) { return -1; } @@ -934,10 +936,14 @@ static int gitdiff_hdrend(const char *line, struct patch *patch) #define DIFF_OLD_NAME 0 #define DIFF_NEW_NAME 1 -static void gitdiff_verify_name(const char *line, int isnull, char **name, int side) +static void gitdiff_verify_name(struct apply_state *state, + const char *line, + int isnull, + char **name, + int side) { if (!*name && !isnull) { - *name = find_name(line, NULL, state_p_value, TERM_TAB); + *name = find_name(line, NULL, state->p_value, TERM_TAB); return; } @@ -947,7 +953,7 @@ static void gitdiff_verify_name(const char *line, int isnull, char **name, int s if (isnull) die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), *name, state_linenr); - another = find_name(line, NULL, state_p_value, TERM_TAB); + another = find_name(line, NULL, state->p_value, TERM_TAB); if (!another || memcmp(another, *name, len + 1)) die((side == DIFF_NEW_NAME) ? _("git apply: bad git-diff - inconsistent new filename on line %d") : @@ -960,81 +966,105 @@ static void gitdiff_verify_name(const char *line, int isnull, char **name, int s } } -static int gitdiff_oldname(const char *line, struct patch *patch) +static int gitdiff_oldname(struct apply_state *state, + const char *line, + struct patch *patch) { - gitdiff_verify_name(line, patch->is_new, &patch->old_name, + gitdiff_verify_name(state, line, + patch->is_new, &patch->old_name, DIFF_OLD_NAME); return 0; } -static int gitdiff_newname(const char *line, struct patch *patch) +static int gitdiff_newname(struct apply_state *state, + const char *line, + struct patch *patch) { - gitdiff_verify_name(line, patch->is_delete, &patch->new_name, + gitdiff_verify_name(state, line, + patch->is_delete, &patch->new_name, DIFF_NEW_NAME); return 0; } -static int gitdiff_oldmode(const char *line, struct patch *patch) +static int gitdiff_oldmode(struct apply_state *state, + const char *line, + struct patch *patch) { patch->old_mode = strtoul(line, NULL, 8); return 0; } -static int gitdiff_newmode(const char *line, struct patch *patch) +static int gitdiff_newmode(struct apply_state *state, + const char *line, + struct patch *patch) { patch->new_mode = strtoul(line, NULL, 8); return 0; } -static int gitdiff_delete(const char *line, struct patch *patch) +static int gitdiff_delete(struct apply_state *state, + const char *line, + struct patch *patch) { patch->is_delete = 1; free(patch->old_name); patch->old_name = xstrdup_or_null(patch->def_name); - return gitdiff_oldmode(line, patch); + return gitdiff_oldmode(state, line, patch); } -static int gitdiff_newfile(const char *line, struct patch *patch) +static int gitdiff_newfile(struct apply_state *state, + const char *line, + struct patch *patch) { patch->is_new = 1; free(patch->new_name); patch->new_name = xstrdup_or_null(patch->def_name); - return gitdiff_newmode(line, patch); + return gitdiff_newmode(state, line, patch); } -static int gitdiff_copysrc(const char *line, struct patch *patch) +static int gitdiff_copysrc(struct apply_state *state, + const char *line, + struct patch *patch) { patch->is_copy = 1; free(patch->old_name); - patch->old_name = find_name(line, NULL, state_p_value ? state_p_value - 1 : 0, 0); + patch->old_name = find_name(line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } -static int gitdiff_copydst(const char *line, struct patch *patch) +static int gitdiff_copydst(struct apply_state *state, + const char *line, + struct patch *patch) { patch->is_copy = 1; free(patch->new_name); - patch->new_name = find_name(line, NULL, state_p_value ? state_p_value - 1 : 0, 0); + patch->new_name = find_name(line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } -static int gitdiff_renamesrc(const char *line, struct patch *patch) +static int gitdiff_renamesrc(struct apply_state *state, + const char *line, + struct patch *patch) { patch->is_rename = 1; free(patch->old_name); - patch->old_name = find_name(line, NULL, state_p_value ? state_p_value - 1 : 0, 0); + patch->old_name = find_name(line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } -static int gitdiff_renamedst(const char *line, struct patch *patch) +static int gitdiff_renamedst(struct apply_state *state, + const char *line, + struct patch *patch) { patch->is_rename = 1; free(patch->new_name); - patch->new_name = find_name(line, NULL, state_p_value ? state_p_value - 1 : 0, 0); + patch->new_name = find_name(line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } -static int gitdiff_similarity(const char *line, struct patch *patch) +static int gitdiff_similarity(struct apply_state *state, + const char *line, + struct patch *patch) { unsigned long val = strtoul(line, NULL, 10); if (val <= 100) @@ -1042,7 +1072,9 @@ static int gitdiff_similarity(const char *line, struct patch *patch) return 0; } -static int gitdiff_dissimilarity(const char *line, struct patch *patch) +static int gitdiff_dissimilarity(struct apply_state *state, + const char *line, + struct patch *patch) { unsigned long val = strtoul(line, NULL, 10); if (val <= 100) @@ -1050,7 +1082,9 @@ static int gitdiff_dissimilarity(const char *line, struct patch *patch) return 0; } -static int gitdiff_index(const char *line, struct patch *patch) +static int gitdiff_index(struct apply_state *state, + const char *line, + struct patch *patch) { /* * index line is N hexadecimal, "..", N hexadecimal, @@ -1087,7 +1121,9 @@ static int gitdiff_index(const char *line, struct patch *patch) * This is normal for a diff that doesn't change anything: we'll fall through * into the next diff. Tell the parser to break out. */ -static int gitdiff_unrecognized(const char *line, struct patch *patch) +static int gitdiff_unrecognized(struct apply_state *state, + const char *line, + struct patch *patch) { return -1; } @@ -1096,15 +1132,17 @@ static int gitdiff_unrecognized(const char *line, struct patch *patch) * Skip p_value leading components from "line"; as we do not accept * absolute paths, return NULL in that case. */ -static const char *skip_tree_prefix(const char *line, int llen) +static const char *skip_tree_prefix(struct apply_state *state, + const char *line, + int llen) { int nslash; int i; - if (!state_p_value) + if (!state->p_value) return (llen && line[0] == '/') ? NULL : line; - nslash = state_p_value; + nslash = state->p_value; for (i = 0; i < llen; i++) { int ch = line[i]; if (ch == '/' && --nslash <= 0) @@ -1121,7 +1159,9 @@ static const char *skip_tree_prefix(const char *line, int llen) * creation or deletion of an empty file. In any of these cases, * both sides are the same name under a/ and b/ respectively. */ -static char *git_header_name(const char *line, int llen) +static char *git_header_name(struct apply_state *state, + const char *line, + int llen) { const char *name; const char *second = NULL; @@ -1139,7 +1179,7 @@ static char *git_header_name(const char *line, int llen) goto free_and_fail1; /* strip the a/b prefix including trailing slash */ - cp = skip_tree_prefix(first.buf, first.len); + cp = skip_tree_prefix(state, first.buf, first.len); if (!cp) goto free_and_fail1; strbuf_remove(&first, 0, cp - first.buf); @@ -1156,7 +1196,7 @@ static char *git_header_name(const char *line, int llen) if (*second == '"') { if (unquote_c_style(&sp, second, NULL)) goto free_and_fail1; - cp = skip_tree_prefix(sp.buf, sp.len); + cp = skip_tree_prefix(state, sp.buf, sp.len); if (!cp) goto free_and_fail1; /* They must match, otherwise ignore */ @@ -1167,7 +1207,7 @@ static char *git_header_name(const char *line, int llen) } /* unquoted second */ - cp = skip_tree_prefix(second, line + llen - second); + cp = skip_tree_prefix(state, second, line + llen - second); if (!cp) goto free_and_fail1; if (line + llen - cp != first.len || @@ -1182,7 +1222,7 @@ static char *git_header_name(const char *line, int llen) } /* unquoted first name */ - name = skip_tree_prefix(line, llen); + name = skip_tree_prefix(state, line, llen); if (!name) return NULL; @@ -1198,7 +1238,7 @@ static char *git_header_name(const char *line, int llen) if (unquote_c_style(&sp, second, NULL)) goto free_and_fail2; - np = skip_tree_prefix(sp.buf, sp.len); + np = skip_tree_prefix(state, sp.buf, sp.len); if (!np) goto free_and_fail2; @@ -1242,7 +1282,7 @@ static char *git_header_name(const char *line, int llen) */ if (!name[len + 1]) return NULL; /* no postimage name */ - second = skip_tree_prefix(name + len + 1, + second = skip_tree_prefix(state, name + len + 1, line_len - (len + 1)); if (!second) return NULL; @@ -1258,7 +1298,11 @@ static char *git_header_name(const char *line, int llen) } /* Verify that we recognize the lines following a git header */ -static int parse_git_header(const char *line, int len, unsigned int size, struct patch *patch) +static int parse_git_header(struct apply_state *state, + const char *line, + int len, + unsigned int size, + struct patch *patch) { unsigned long offset; @@ -1272,7 +1316,7 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct * or removing or adding empty files), so we get * the default name from the header. */ - patch->def_name = git_header_name(line, len); + patch->def_name = git_header_name(state, line, len); if (patch->def_name && root.len) { char *s = xstrfmt("%s%s", root.buf, patch->def_name); free(patch->def_name); @@ -1285,7 +1329,7 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state_linenr++) { static const struct opentry { const char *str; - int (*fn)(const char *, struct patch *); + int (*fn)(struct apply_state *, const char *, struct patch *); } optable[] = { { "@@ -", gitdiff_hdrend }, { "--- ", gitdiff_oldname }, @@ -1315,7 +1359,7 @@ static int parse_git_header(const char *line, int len, unsigned int size, struct int oplen = strlen(p->str); if (len < oplen || memcmp(p->str, line, oplen)) continue; - if (p->fn(line + oplen, patch) < 0) + if (p->fn(state, line + oplen, patch) < 0) return offset; break; } @@ -1485,7 +1529,7 @@ static int find_header(struct apply_state *state, * or mode change, so we handle that specially */ if (!memcmp("diff --git ", line, 11)) { - int git_hdr_len = parse_git_header(line, len, size, patch); + int git_hdr_len = parse_git_header(state, line, len, size, patch); if (git_hdr_len <= len) continue; if (!patch->old_name && !patch->new_name) { @@ -1494,8 +1538,8 @@ static int find_header(struct apply_state *state, "%d leading pathname component (line %d)", "git diff header lacks filename information when removing " "%d leading pathname components (line %d)", - state_p_value), - state_p_value, state_linenr); + state->p_value), + state->p_value, state_linenr); patch->old_name = xstrdup(patch->def_name); patch->new_name = xstrdup(patch->def_name); } @@ -4539,9 +4583,11 @@ static int option_parse_include(const struct option *opt, } static int option_parse_p(const struct option *opt, - const char *arg, int unset) + const char *arg, + int unset) { - state_p_value = atoi(arg); + struct apply_state *state = opt->value; + state->p_value = atoi(arg); p_value_known = 1; return 0; } @@ -4582,6 +4628,7 @@ static void init_apply_state(struct apply_state *state, const char *prefix) state->prefix_length = state->prefix ? strlen(state->prefix) : 0; state->apply = 1; state->line_termination = '\n'; + state->p_value = 1; state->p_context = UINT_MAX; git_apply_config(); @@ -4615,7 +4662,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) { OPTION_CALLBACK, 0, "include", &state, N_("path"), N_("apply changes matching the given path"), 0, option_parse_include }, - { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"), + { OPTION_CALLBACK, 'p', NULL, &state, N_("num"), N_("remove leading slashes from traditional diff paths"), 0, option_parse_p }, OPT_BOOL(0, "no-add", &state.no_add, -- cgit v1.2.1 From b76184e41003c6bd4731d79474c72449f854b37c Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:10 +0200 Subject: builtin/apply: move 'p_value_known' global into 'struct apply_state' To libify the apply functionality the 'p_value_known' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 843fafd629..e1b68d4cb7 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -52,6 +52,7 @@ struct apply_state { const char *patch_input_file; int line_termination; int p_value; + int p_value_known; unsigned int p_context; /* Exclude and include path parameters */ @@ -61,8 +62,6 @@ struct apply_state { static int newfd = -1; -static int p_value_known; - static const char * const apply_usage[] = { N_("git apply [] [...]"), NULL @@ -875,14 +874,14 @@ static void parse_traditional_patch(struct apply_state *state, first += 4; /* skip "--- " */ second += 4; /* skip "+++ " */ - if (!p_value_known) { + if (!state->p_value_known) { int p, q; p = guess_p_value(state, first); q = guess_p_value(state, second); if (p < 0) p = q; if (0 <= p && p == q) { state->p_value = p; - p_value_known = 1; + state->p_value_known = 1; } } if (is_dev_null(first)) { @@ -4588,7 +4587,7 @@ static int option_parse_p(const struct option *opt, { struct apply_state *state = opt->value; state->p_value = atoi(arg); - p_value_known = 1; + state->p_value_known = 1; return 0; } -- cgit v1.2.1 From 36371e4c7ef87a2895b954284c29e8beff576084 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:11 +0200 Subject: builtin/apply: move 'root' global into 'struct apply_state' To libify the apply functionality the 'root' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 82 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index e1b68d4cb7..5dc1d899f8 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -51,6 +51,7 @@ struct apply_state { const char *fake_ancestor; const char *patch_input_file; int line_termination; + struct strbuf root; int p_value; int p_value_known; unsigned int p_context; @@ -83,8 +84,6 @@ static enum ws_ignore { } ws_ignore_action = ignore_ws_none; -static struct strbuf root = STRBUF_INIT; - static void parse_whitespace_option(const char *option) { if (!option) { @@ -474,7 +473,10 @@ static char *squash_slash(char *name) return name; } -static char *find_name_gnu(const char *line, const char *def, int p_value) +static char *find_name_gnu(struct apply_state *state, + const char *line, + const char *def, + int p_value) { struct strbuf name = STRBUF_INIT; char *cp; @@ -498,8 +500,8 @@ static char *find_name_gnu(const char *line, const char *def, int p_value) } strbuf_remove(&name, 0, cp - name.buf); - if (root.len) - strbuf_insert(&name, 0, root.buf, root.len); + if (state->root.len) + strbuf_insert(&name, 0, state->root.buf, state->root.len); return squash_slash(strbuf_detach(&name, NULL)); } @@ -662,8 +664,12 @@ static size_t diff_timestamp_len(const char *line, size_t len) return line + len - end; } -static char *find_name_common(const char *line, const char *def, - int p_value, const char *end, int terminate) +static char *find_name_common(struct apply_state *state, + const char *line, + const char *def, + int p_value, + const char *end, + int terminate) { int len; const char *start = NULL; @@ -701,32 +707,39 @@ static char *find_name_common(const char *line, const char *def, return squash_slash(xstrdup(def)); } - if (root.len) { - char *ret = xstrfmt("%s%.*s", root.buf, len, start); + if (state->root.len) { + char *ret = xstrfmt("%s%.*s", state->root.buf, len, start); return squash_slash(ret); } return squash_slash(xmemdupz(start, len)); } -static char *find_name(const char *line, char *def, int p_value, int terminate) +static char *find_name(struct apply_state *state, + const char *line, + char *def, + int p_value, + int terminate) { if (*line == '"') { - char *name = find_name_gnu(line, def, p_value); + char *name = find_name_gnu(state, line, def, p_value); if (name) return name; } - return find_name_common(line, def, p_value, NULL, terminate); + return find_name_common(state, line, def, p_value, NULL, terminate); } -static char *find_name_traditional(const char *line, char *def, int p_value) +static char *find_name_traditional(struct apply_state *state, + const char *line, + char *def, + int p_value) { size_t len; size_t date_len; if (*line == '"') { - char *name = find_name_gnu(line, def, p_value); + char *name = find_name_gnu(state, line, def, p_value); if (name) return name; } @@ -734,10 +747,10 @@ static char *find_name_traditional(const char *line, char *def, int p_value) len = strchrnul(line, '\n') - line; date_len = diff_timestamp_len(line, len); if (!date_len) - return find_name_common(line, def, p_value, NULL, TERM_TAB); + return find_name_common(state, line, def, p_value, NULL, TERM_TAB); len -= date_len; - return find_name_common(line, def, p_value, line + len, 0); + return find_name_common(state, line, def, p_value, line + len, 0); } static int count_slashes(const char *cp) @@ -762,7 +775,7 @@ static int guess_p_value(struct apply_state *state, const char *nameline) if (is_dev_null(nameline)) return -1; - name = find_name_traditional(nameline, NULL, 0); + name = find_name_traditional(state, nameline, NULL, 0); if (!name) return -1; cp = strchr(name, '/'); @@ -887,17 +900,17 @@ static void parse_traditional_patch(struct apply_state *state, if (is_dev_null(first)) { patch->is_new = 1; patch->is_delete = 0; - name = find_name_traditional(second, NULL, state->p_value); + name = find_name_traditional(state, second, NULL, state->p_value); patch->new_name = name; } else if (is_dev_null(second)) { patch->is_new = 0; patch->is_delete = 1; - name = find_name_traditional(first, NULL, state->p_value); + name = find_name_traditional(state, first, NULL, state->p_value); patch->old_name = name; } else { char *first_name; - first_name = find_name_traditional(first, NULL, state->p_value); - name = find_name_traditional(second, first_name, state->p_value); + first_name = find_name_traditional(state, first, NULL, state->p_value); + name = find_name_traditional(state, second, first_name, state->p_value); free(first_name); if (has_epoch_timestamp(first)) { patch->is_new = 1; @@ -942,7 +955,7 @@ static void gitdiff_verify_name(struct apply_state *state, int side) { if (!*name && !isnull) { - *name = find_name(line, NULL, state->p_value, TERM_TAB); + *name = find_name(state, line, NULL, state->p_value, TERM_TAB); return; } @@ -952,7 +965,7 @@ static void gitdiff_verify_name(struct apply_state *state, if (isnull) die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), *name, state_linenr); - another = find_name(line, NULL, state->p_value, TERM_TAB); + another = find_name(state, line, NULL, state->p_value, TERM_TAB); if (!another || memcmp(another, *name, len + 1)) die((side == DIFF_NEW_NAME) ? _("git apply: bad git-diff - inconsistent new filename on line %d") : @@ -1027,7 +1040,7 @@ static int gitdiff_copysrc(struct apply_state *state, { patch->is_copy = 1; free(patch->old_name); - patch->old_name = find_name(line, NULL, state->p_value ? state->p_value - 1 : 0, 0); + patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } @@ -1037,7 +1050,7 @@ static int gitdiff_copydst(struct apply_state *state, { patch->is_copy = 1; free(patch->new_name); - patch->new_name = find_name(line, NULL, state->p_value ? state->p_value - 1 : 0, 0); + patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } @@ -1047,7 +1060,7 @@ static int gitdiff_renamesrc(struct apply_state *state, { patch->is_rename = 1; free(patch->old_name); - patch->old_name = find_name(line, NULL, state->p_value ? state->p_value - 1 : 0, 0); + patch->old_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } @@ -1057,7 +1070,7 @@ static int gitdiff_renamedst(struct apply_state *state, { patch->is_rename = 1; free(patch->new_name); - patch->new_name = find_name(line, NULL, state->p_value ? state->p_value - 1 : 0, 0); + patch->new_name = find_name(state, line, NULL, state->p_value ? state->p_value - 1 : 0, 0); return 0; } @@ -1316,8 +1329,8 @@ static int parse_git_header(struct apply_state *state, * the default name from the header. */ patch->def_name = git_header_name(state, line, len); - if (patch->def_name && root.len) { - char *s = xstrfmt("%s%s", root.buf, patch->def_name); + if (patch->def_name && state->root.len) { + char *s = xstrfmt("%s%s", state->root.buf, patch->def_name); free(patch->def_name); patch->def_name = s; } @@ -4614,9 +4627,10 @@ static int option_parse_whitespace(const struct option *opt, static int option_parse_directory(const struct option *opt, const char *arg, int unset) { - strbuf_reset(&root); - strbuf_addstr(&root, arg); - strbuf_complete(&root, '/'); + struct apply_state *state = opt->value; + strbuf_reset(&state->root); + strbuf_addstr(&state->root, arg); + strbuf_complete(&state->root, '/'); return 0; } @@ -4629,6 +4643,7 @@ static void init_apply_state(struct apply_state *state, const char *prefix) state->line_termination = '\n'; state->p_value = 1; state->p_context = UINT_MAX; + strbuf_init(&state->root, 0); git_apply_config(); if (apply_default_whitespace) @@ -4640,6 +4655,7 @@ static void init_apply_state(struct apply_state *state, const char *prefix) static void clear_apply_state(struct apply_state *state) { string_list_clear(&state->limit_by_name, 0); + strbuf_release(&state->root); } int cmd_apply(int argc, const char **argv, const char *prefix) @@ -4717,7 +4733,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) OPT_BIT(0, "recount", &options, N_("do not trust the line counts in the hunk headers"), RECOUNT), - { OPTION_CALLBACK, 0, "directory", NULL, N_("root"), + { OPTION_CALLBACK, 0, "directory", &state, N_("root"), N_("prepend to all filenames"), 0, option_parse_directory }, OPT_END() -- cgit v1.2.1 From 5460cd0b1000078846e2850eeaba3573965c1faf Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:12 +0200 Subject: builtin/apply: move 'whitespace_error' global into 'struct apply_state' To libify the apply functionality the 'whitespace_error' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 5dc1d899f8..44717b2fdf 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -59,6 +59,9 @@ struct apply_state { /* Exclude and include path parameters */ struct string_list limit_by_name; int has_include; + + /* These control whitespace errors */ + int whitespace_error; }; static int newfd = -1; @@ -74,7 +77,6 @@ static enum ws_error_action { die_on_ws_error, correct_ws_error } ws_error_action = warn_on_ws_error; -static int whitespace_error; static int squelch_whitespace_errors = 5; static int applied_after_fixing_ws; @@ -1596,9 +1598,9 @@ static void record_ws_error(struct apply_state *state, if (!result) return; - whitespace_error++; + state->whitespace_error++; if (squelch_whitespace_errors && - squelch_whitespace_errors < whitespace_error) + squelch_whitespace_errors < state->whitespace_error) return; err = whitespace_error_string(result); @@ -2855,7 +2857,7 @@ static int apply_one_fragment(struct apply_state *state, start = newlines.len; if (first != '+' || - !whitespace_error || + !state->whitespace_error || ws_error_action != correct_ws_error) { strbuf_add(&newlines, patch + 1, plen); } @@ -4528,7 +4530,7 @@ static int apply_patch(struct apply_state *state, if (!list && !skipped_patch) die(_("unrecognized input")); - if (whitespace_error && (ws_error_action == die_on_ws_error)) + if (state->whitespace_error && (ws_error_action == die_on_ws_error)) state->apply = 0; state->update_index = state->check_index && state->apply; @@ -4791,11 +4793,11 @@ int cmd_apply(int argc, const char **argv, const char *prefix) set_default_whitespace_mode(&state, whitespace_option); if (read_stdin) errs |= apply_patch(&state, 0, "", options); - if (whitespace_error) { + if (state.whitespace_error) { if (squelch_whitespace_errors && - squelch_whitespace_errors < whitespace_error) { + squelch_whitespace_errors < state.whitespace_error) { int squelched = - whitespace_error - squelch_whitespace_errors; + state.whitespace_error - squelch_whitespace_errors; warning(Q_("squelched %d whitespace error", "squelched %d whitespace errors", squelched), @@ -4804,18 +4806,18 @@ int cmd_apply(int argc, const char **argv, const char *prefix) if (ws_error_action == die_on_ws_error) die(Q_("%d line adds whitespace errors.", "%d lines add whitespace errors.", - whitespace_error), - whitespace_error); + state.whitespace_error), + state.whitespace_error); if (applied_after_fixing_ws && state.apply) warning("%d line%s applied after" " fixing whitespace errors.", applied_after_fixing_ws, applied_after_fixing_ws == 1 ? "" : "s"); - else if (whitespace_error) + else if (state.whitespace_error) warning(Q_("%d line adds whitespace errors.", "%d lines add whitespace errors.", - whitespace_error), - whitespace_error); + state.whitespace_error), + state.whitespace_error); } if (state.update_index) { -- cgit v1.2.1 From 161fcbe9884466e95cf5b1c7cd22f64a16a24045 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:13 +0200 Subject: builtin/apply: move 'whitespace_option' into 'struct apply_state' This will enable further refactoring, and it is more coherent and simpler if all the option_parse_*() functions are passed a 'struct apply_state' instance in opt->value. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 44717b2fdf..78205f8053 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -61,6 +61,7 @@ struct apply_state { int has_include; /* These control whitespace errors */ + const char *whitespace_option; int whitespace_error; }; @@ -4619,9 +4620,9 @@ static int option_parse_space_change(const struct option *opt, static int option_parse_whitespace(const struct option *opt, const char *arg, int unset) { - const char **whitespace_option = opt->value; + struct apply_state *state = opt->value; - *whitespace_option = arg; + state->whitespace_option = arg; parse_whitespace_option(arg); return 0; } @@ -4670,8 +4671,6 @@ int cmd_apply(int argc, const char **argv, const char *prefix) int read_stdin = 1; struct apply_state state; - const char *whitespace_option = NULL; - struct option builtin_apply_options[] = { { OPTION_CALLBACK, 0, "exclude", &state, N_("path"), N_("don't apply changes matching the given path"), @@ -4711,7 +4710,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) N_("paths are separated with NUL character"), '\0'), OPT_INTEGER('C', NULL, &state.p_context, N_("ensure at least lines of context match")), - { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"), + { OPTION_CALLBACK, 0, "whitespace", &state, N_("action"), N_("detect new or modified lines that have whitespace errors"), 0, option_parse_whitespace }, { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL, @@ -4786,11 +4785,11 @@ int cmd_apply(int argc, const char **argv, const char *prefix) if (fd < 0) die_errno(_("can't open patch '%s'"), arg); read_stdin = 0; - set_default_whitespace_mode(&state, whitespace_option); + set_default_whitespace_mode(&state, state.whitespace_option); errs |= apply_patch(&state, fd, arg, options); close(fd); } - set_default_whitespace_mode(&state, whitespace_option); + set_default_whitespace_mode(&state, state.whitespace_option); if (read_stdin) errs |= apply_patch(&state, 0, "", options); if (state.whitespace_error) { -- cgit v1.2.1 From 8bcba3d0d60d859c1494cdb59301cf84befcdfaa Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:14 +0200 Subject: builtin/apply: remove whitespace_option arg from set_default_whitespace_mode() A previous change has move the whitespace_option variable from cmd_apply into 'struct apply_state', so that we can now avoid passing it separately to set_default_whitespace_mode(). Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 78205f8053..523ed74a6c 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -132,10 +132,9 @@ static void parse_ignorewhitespace_option(const char *option) die(_("unrecognized whitespace ignore option '%s'"), option); } -static void set_default_whitespace_mode(struct apply_state *state, - const char *whitespace_option) +static void set_default_whitespace_mode(struct apply_state *state) { - if (!whitespace_option && !apply_default_whitespace) + if (!state->whitespace_option && !apply_default_whitespace) ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error); } @@ -4785,11 +4784,11 @@ int cmd_apply(int argc, const char **argv, const char *prefix) if (fd < 0) die_errno(_("can't open patch '%s'"), arg); read_stdin = 0; - set_default_whitespace_mode(&state, state.whitespace_option); + set_default_whitespace_mode(&state); errs |= apply_patch(&state, fd, arg, options); close(fd); } - set_default_whitespace_mode(&state, state.whitespace_option); + set_default_whitespace_mode(&state); if (read_stdin) errs |= apply_patch(&state, 0, "", options); if (state.whitespace_error) { -- cgit v1.2.1 From 70e1d53df17bc8fa81f3583a50f76753a8bac154 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:15 +0200 Subject: builtin/apply: move 'squelch_whitespace_errors' into 'struct apply_state' To libify the apply functionality the 'squelch_whitespace_errors' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 523ed74a6c..619b8fb125 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -63,6 +63,7 @@ struct apply_state { /* These control whitespace errors */ const char *whitespace_option; int whitespace_error; + int squelch_whitespace_errors; }; static int newfd = -1; @@ -78,7 +79,6 @@ static enum ws_error_action { die_on_ws_error, correct_ws_error } ws_error_action = warn_on_ws_error; -static int squelch_whitespace_errors = 5; static int applied_after_fixing_ws; static enum ws_ignore { @@ -87,7 +87,7 @@ static enum ws_ignore { } ws_ignore_action = ignore_ws_none; -static void parse_whitespace_option(const char *option) +static void parse_whitespace_option(struct apply_state *state, const char *option) { if (!option) { ws_error_action = warn_on_ws_error; @@ -107,7 +107,7 @@ static void parse_whitespace_option(const char *option) } if (!strcmp(option, "error-all")) { ws_error_action = die_on_ws_error; - squelch_whitespace_errors = 0; + state->squelch_whitespace_errors = 0; return; } if (!strcmp(option, "strip") || !strcmp(option, "fix")) { @@ -1599,8 +1599,8 @@ static void record_ws_error(struct apply_state *state, return; state->whitespace_error++; - if (squelch_whitespace_errors && - squelch_whitespace_errors < state->whitespace_error) + if (state->squelch_whitespace_errors && + state->squelch_whitespace_errors < state->whitespace_error) return; err = whitespace_error_string(result); @@ -4620,9 +4620,8 @@ static int option_parse_whitespace(const struct option *opt, const char *arg, int unset) { struct apply_state *state = opt->value; - state->whitespace_option = arg; - parse_whitespace_option(arg); + parse_whitespace_option(state, arg); return 0; } @@ -4645,11 +4644,12 @@ static void init_apply_state(struct apply_state *state, const char *prefix) state->line_termination = '\n'; state->p_value = 1; state->p_context = UINT_MAX; + state->squelch_whitespace_errors = 5; strbuf_init(&state->root, 0); git_apply_config(); if (apply_default_whitespace) - parse_whitespace_option(apply_default_whitespace); + parse_whitespace_option(state, apply_default_whitespace); if (apply_default_ignorewhitespace) parse_ignorewhitespace_option(apply_default_ignorewhitespace); } @@ -4792,10 +4792,10 @@ int cmd_apply(int argc, const char **argv, const char *prefix) if (read_stdin) errs |= apply_patch(&state, 0, "", options); if (state.whitespace_error) { - if (squelch_whitespace_errors && - squelch_whitespace_errors < state.whitespace_error) { + if (state.squelch_whitespace_errors && + state.squelch_whitespace_errors < state.whitespace_error) { int squelched = - state.whitespace_error - squelch_whitespace_errors; + state.whitespace_error - state.squelch_whitespace_errors; warning(Q_("squelched %d whitespace error", "squelched %d whitespace errors", squelched), -- cgit v1.2.1 From 7243f5f3507abe3d083ec18e44bc7cb464fa06f4 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:16 +0200 Subject: builtin/apply: move 'applied_after_fixing_ws' into 'struct apply_state' To libify the apply functionality the 'applied_after_fixing_ws' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 619b8fb125..91b6283a97 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -64,6 +64,7 @@ struct apply_state { const char *whitespace_option; int whitespace_error; int squelch_whitespace_errors; + int applied_after_fixing_ws; }; static int newfd = -1; @@ -79,7 +80,6 @@ static enum ws_error_action { die_on_ws_error, correct_ws_error } ws_error_action = warn_on_ws_error; -static int applied_after_fixing_ws; static enum ws_ignore { ignore_ws_none, @@ -2862,7 +2862,7 @@ static int apply_one_fragment(struct apply_state *state, strbuf_add(&newlines, patch + 1, plen); } else { - ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws); + ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &state->applied_after_fixing_ws); } add_line_info(&postimage, newlines.buf + start, newlines.len - start, (first == '+' ? 0 : LINE_COMMON)); @@ -4806,11 +4806,11 @@ int cmd_apply(int argc, const char **argv, const char *prefix) "%d lines add whitespace errors.", state.whitespace_error), state.whitespace_error); - if (applied_after_fixing_ws && state.apply) + if (state.applied_after_fixing_ws && state.apply) warning("%d line%s applied after" " fixing whitespace errors.", - applied_after_fixing_ws, - applied_after_fixing_ws == 1 ? "" : "s"); + state.applied_after_fixing_ws, + state.applied_after_fixing_ws == 1 ? "" : "s"); else if (state.whitespace_error) warning(Q_("%d line adds whitespace errors.", "%d lines add whitespace errors.", -- cgit v1.2.1 From e9c6b279b8226b2ab93a6a550a654640ccc5b256 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:17 +0200 Subject: builtin/apply: move 'ws_error_action' into 'struct apply_state' To libify the apply functionality the 'ws_error_action' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 61 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 91b6283a97..61d809a99e 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -21,6 +21,13 @@ #include "ll-merge.h" #include "rerere.h" +enum ws_error_action { + nowarn_ws_error, + warn_on_ws_error, + die_on_ws_error, + correct_ws_error +}; + struct apply_state { const char *prefix; int prefix_length; @@ -61,6 +68,7 @@ struct apply_state { int has_include; /* These control whitespace errors */ + enum ws_error_action ws_error_action; const char *whitespace_option; int whitespace_error; int squelch_whitespace_errors; @@ -74,12 +82,6 @@ static const char * const apply_usage[] = { NULL }; -static enum ws_error_action { - nowarn_ws_error, - warn_on_ws_error, - die_on_ws_error, - correct_ws_error -} ws_error_action = warn_on_ws_error; static enum ws_ignore { ignore_ws_none, @@ -90,28 +92,28 @@ static enum ws_ignore { static void parse_whitespace_option(struct apply_state *state, const char *option) { if (!option) { - ws_error_action = warn_on_ws_error; + state->ws_error_action = warn_on_ws_error; return; } if (!strcmp(option, "warn")) { - ws_error_action = warn_on_ws_error; + state->ws_error_action = warn_on_ws_error; return; } if (!strcmp(option, "nowarn")) { - ws_error_action = nowarn_ws_error; + state->ws_error_action = nowarn_ws_error; return; } if (!strcmp(option, "error")) { - ws_error_action = die_on_ws_error; + state->ws_error_action = die_on_ws_error; return; } if (!strcmp(option, "error-all")) { - ws_error_action = die_on_ws_error; + state->ws_error_action = die_on_ws_error; state->squelch_whitespace_errors = 0; return; } if (!strcmp(option, "strip") || !strcmp(option, "fix")) { - ws_error_action = correct_ws_error; + state->ws_error_action = correct_ws_error; return; } die(_("unrecognized whitespace option '%s'"), option); @@ -135,7 +137,7 @@ static void parse_ignorewhitespace_option(const char *option) static void set_default_whitespace_mode(struct apply_state *state) { if (!state->whitespace_option && !apply_default_whitespace) - ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error); + state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error); } /* @@ -1670,12 +1672,12 @@ static int parse_fragment(struct apply_state *state, leading++; trailing++; if (!state->apply_in_reverse && - ws_error_action == correct_ws_error) + state->ws_error_action == correct_ws_error) check_whitespace(state, line, len, patch->ws_rule); break; case '-': if (state->apply_in_reverse && - ws_error_action != nowarn_ws_error) + state->ws_error_action != nowarn_ws_error) check_whitespace(state, line, len, patch->ws_rule); deleted++; oldlines--; @@ -1683,7 +1685,7 @@ static int parse_fragment(struct apply_state *state, break; case '+': if (!state->apply_in_reverse && - ws_error_action != nowarn_ws_error) + state->ws_error_action != nowarn_ws_error) check_whitespace(state, line, len, patch->ws_rule); added++; newlines--; @@ -2396,7 +2398,8 @@ static int line_by_line_fuzzy_match(struct image *img, return 1; } -static int match_fragment(struct image *img, +static int match_fragment(struct apply_state *state, + struct image *img, struct image *preimage, struct image *postimage, unsigned long try, @@ -2417,7 +2420,7 @@ static int match_fragment(struct image *img, preimage_limit = preimage->nr; if (match_end && (preimage->nr + try_lno != img->nr)) return 0; - } else if (ws_error_action == correct_ws_error && + } else if (state->ws_error_action == correct_ws_error && (ws_rule & WS_BLANK_AT_EOF)) { /* * This hunk extends beyond the end of img, and we are @@ -2489,7 +2492,7 @@ static int match_fragment(struct image *img, return line_by_line_fuzzy_match(img, preimage, postimage, try, try_lno, preimage_limit); - if (ws_error_action != correct_ws_error) + if (state->ws_error_action != correct_ws_error) return 0; /* @@ -2601,7 +2604,8 @@ static int match_fragment(struct image *img, return 0; } -static int find_pos(struct image *img, +static int find_pos(struct apply_state *state, + struct image *img, struct image *preimage, struct image *postimage, int line, @@ -2645,7 +2649,7 @@ static int find_pos(struct image *img, try_lno = line; for (i = 0; ; i++) { - if (match_fragment(img, preimage, postimage, + if (match_fragment(state, img, preimage, postimage, try, try_lno, ws_rule, match_beginning, match_end)) return try_lno; @@ -2858,7 +2862,7 @@ static int apply_one_fragment(struct apply_state *state, start = newlines.len; if (first != '+' || !state->whitespace_error || - ws_error_action != correct_ws_error) { + state->ws_error_action != correct_ws_error) { strbuf_add(&newlines, patch + 1, plen); } else { @@ -2936,7 +2940,7 @@ static int apply_one_fragment(struct apply_state *state, for (;;) { - applied_pos = find_pos(img, &preimage, &postimage, pos, + applied_pos = find_pos(state, img, &preimage, &postimage, pos, ws_rule, match_beginning, match_end); if (applied_pos >= 0) @@ -2972,10 +2976,10 @@ static int apply_one_fragment(struct apply_state *state, if (new_blank_lines_at_end && preimage.nr + applied_pos >= img->nr && (ws_rule & WS_BLANK_AT_EOF) && - ws_error_action != nowarn_ws_error) { + state->ws_error_action != nowarn_ws_error) { record_ws_error(state, WS_BLANK_AT_EOF, "+", 1, found_new_blank_lines_at_end); - if (ws_error_action == correct_ws_error) { + if (state->ws_error_action == correct_ws_error) { while (new_blank_lines_at_end--) remove_last_line(&postimage); } @@ -2986,7 +2990,7 @@ static int apply_one_fragment(struct apply_state *state, * apply_patch->check_patch_list->check_patch-> * apply_data->apply_fragments->apply_one_fragment */ - if (ws_error_action == die_on_ws_error) + if (state->ws_error_action == die_on_ws_error) state->apply = 0; } @@ -4530,7 +4534,7 @@ static int apply_patch(struct apply_state *state, if (!list && !skipped_patch) die(_("unrecognized input")); - if (state->whitespace_error && (ws_error_action == die_on_ws_error)) + if (state->whitespace_error && (state->ws_error_action == die_on_ws_error)) state->apply = 0; state->update_index = state->check_index && state->apply; @@ -4645,6 +4649,7 @@ static void init_apply_state(struct apply_state *state, const char *prefix) state->p_value = 1; state->p_context = UINT_MAX; state->squelch_whitespace_errors = 5; + state->ws_error_action = warn_on_ws_error; strbuf_init(&state->root, 0); git_apply_config(); @@ -4801,7 +4806,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) squelched), squelched); } - if (ws_error_action == die_on_ws_error) + if (state.ws_error_action == die_on_ws_error) die(Q_("%d line adds whitespace errors.", "%d lines add whitespace errors.", state.whitespace_error), -- cgit v1.2.1 From 10a9ddba2c3478c8080acc1b2c83d75d09ee2492 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:18 +0200 Subject: builtin/apply: move 'ws_ignore_action' into 'struct apply_state' To libify the apply functionality the 'ws_ignore_action' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 61d809a99e..e5bc9cc1cc 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -28,6 +28,12 @@ enum ws_error_action { correct_ws_error }; + +enum ws_ignore { + ignore_ws_none, + ignore_ws_change +}; + struct apply_state { const char *prefix; int prefix_length; @@ -69,6 +75,7 @@ struct apply_state { /* These control whitespace errors */ enum ws_error_action ws_error_action; + enum ws_ignore ws_ignore_action; const char *whitespace_option; int whitespace_error; int squelch_whitespace_errors; @@ -82,13 +89,6 @@ static const char * const apply_usage[] = { NULL }; - -static enum ws_ignore { - ignore_ws_none, - ignore_ws_change -} ws_ignore_action = ignore_ws_none; - - static void parse_whitespace_option(struct apply_state *state, const char *option) { if (!option) { @@ -119,16 +119,17 @@ static void parse_whitespace_option(struct apply_state *state, const char *optio die(_("unrecognized whitespace option '%s'"), option); } -static void parse_ignorewhitespace_option(const char *option) +static void parse_ignorewhitespace_option(struct apply_state *state, + const char *option) { if (!option || !strcmp(option, "no") || !strcmp(option, "false") || !strcmp(option, "never") || !strcmp(option, "none")) { - ws_ignore_action = ignore_ws_none; + state->ws_ignore_action = ignore_ws_none; return; } if (!strcmp(option, "change")) { - ws_ignore_action = ignore_ws_change; + state->ws_ignore_action = ignore_ws_change; return; } die(_("unrecognized whitespace ignore option '%s'"), option); @@ -2488,7 +2489,7 @@ static int match_fragment(struct apply_state *state, * fuzzy matching. We collect all the line length information because * we need it to adjust whitespace if we match. */ - if (ws_ignore_action == ignore_ws_change) + if (state->ws_ignore_action == ignore_ws_change) return line_by_line_fuzzy_match(img, preimage, postimage, try, try_lno, preimage_limit); @@ -4611,12 +4612,13 @@ static int option_parse_p(const struct option *opt, } static int option_parse_space_change(const struct option *opt, - const char *arg, int unset) + const char *arg, int unset) { + struct apply_state *state = opt->value; if (unset) - ws_ignore_action = ignore_ws_none; + state->ws_ignore_action = ignore_ws_none; else - ws_ignore_action = ignore_ws_change; + state->ws_ignore_action = ignore_ws_change; return 0; } @@ -4650,13 +4652,14 @@ static void init_apply_state(struct apply_state *state, const char *prefix) state->p_context = UINT_MAX; state->squelch_whitespace_errors = 5; state->ws_error_action = warn_on_ws_error; + state->ws_ignore_action = ignore_ws_none; strbuf_init(&state->root, 0); git_apply_config(); if (apply_default_whitespace) parse_whitespace_option(state, apply_default_whitespace); if (apply_default_ignorewhitespace) - parse_ignorewhitespace_option(apply_default_ignorewhitespace); + parse_ignorewhitespace_option(state, apply_default_ignorewhitespace); } static void clear_apply_state(struct apply_state *state) @@ -4717,10 +4720,10 @@ int cmd_apply(int argc, const char **argv, const char *prefix) { OPTION_CALLBACK, 0, "whitespace", &state, N_("action"), N_("detect new or modified lines that have whitespace errors"), 0, option_parse_whitespace }, - { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL, + { OPTION_CALLBACK, 0, "ignore-space-change", &state, NULL, N_("ignore changes in whitespace when finding context"), PARSE_OPT_NOARG, option_parse_space_change }, - { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL, + { OPTION_CALLBACK, 0, "ignore-whitespace", &state, NULL, N_("ignore changes in whitespace when finding context"), PARSE_OPT_NOARG, option_parse_space_change }, OPT_BOOL('R', "reverse", &state.apply_in_reverse, -- cgit v1.2.1 From 1ffec303ab043c392e480d37df2b34e40f3d4bd0 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:19 +0200 Subject: builtin/apply: move 'max_change' and 'max_len' into 'struct apply_state' To libify the apply functionality the 'max_change' and 'max_len' variables should not be static and global to the file. Let's move them into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index e5bc9cc1cc..9e7d1810e5 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -73,6 +73,14 @@ struct apply_state { struct string_list limit_by_name; int has_include; + /* + * For "diff-stat" like behaviour, we keep track of the biggest change + * we've seen, and the longest filename. That allows us to do simple + * scaling. + */ + int max_change; + int max_len; + /* These control whitespace errors */ enum ws_error_action ws_error_action; enum ws_ignore ws_ignore_action; @@ -141,13 +149,6 @@ static void set_default_whitespace_mode(struct apply_state *state) state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error); } -/* - * For "diff-stat" like behaviour, we keep track of the biggest change - * we've seen, and the longest filename. That allows us to do simple - * scaling. - */ -static int max_change, max_len; - /* * Various "current state", notably line numbers and what * file (and how) we're patching right now.. The "is_xxxx" @@ -2172,7 +2173,7 @@ static const char pluses[] = static const char minuses[]= "----------------------------------------------------------------------"; -static void show_stats(struct patch *patch) +static void show_stats(struct apply_state *state, struct patch *patch) { struct strbuf qname = STRBUF_INIT; char *cp = patch->new_name ? patch->new_name : patch->old_name; @@ -2183,7 +2184,7 @@ static void show_stats(struct patch *patch) /* * "scale" the filename */ - max = max_len; + max = state->max_len; if (max > 50) max = 50; @@ -2206,13 +2207,13 @@ static void show_stats(struct patch *patch) /* * scale the add/delete */ - max = max + max_change > 70 ? 70 - max : max_change; + max = max + state->max_change > 70 ? 70 - max : state->max_change; add = patch->lines_added; del = patch->lines_deleted; - if (max_change > 0) { - int total = ((add + del) * max + max_change / 2) / max_change; - add = (add * max + max_change / 2) / max_change; + if (state->max_change > 0) { + int total = ((add + del) * max + state->max_change / 2) / state->max_change; + add = (add * max + state->max_change / 2) / state->max_change; del = total - add; } printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted, @@ -4038,7 +4039,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename) discard_index(&result); } -static void stat_patch_list(struct patch *patch) +static void stat_patch_list(struct apply_state *state, struct patch *patch) { int files, adds, dels; @@ -4046,7 +4047,7 @@ static void stat_patch_list(struct patch *patch) files++; adds += patch->lines_added; dels += patch->lines_deleted; - show_stats(patch); + show_stats(state, patch); } print_stat_summary(stdout, files, adds, dels); @@ -4144,25 +4145,25 @@ static void summary_patch_list(struct patch *patch) } } -static void patch_stats(struct patch *patch) +static void patch_stats(struct apply_state *state, struct patch *patch) { int lines = patch->lines_added + patch->lines_deleted; - if (lines > max_change) - max_change = lines; + if (lines > state->max_change) + state->max_change = lines; if (patch->old_name) { int len = quote_c_style(patch->old_name, NULL, NULL, 0); if (!len) len = strlen(patch->old_name); - if (len > max_len) - max_len = len; + if (len > state->max_len) + state->max_len = len; } if (patch->new_name) { int len = quote_c_style(patch->new_name, NULL, NULL, 0); if (!len) len = strlen(patch->new_name); - if (len > max_len) - max_len = len; + if (len > state->max_len) + state->max_len = len; } } @@ -4519,7 +4520,7 @@ static int apply_patch(struct apply_state *state, if (state->apply_in_reverse) reverse_patches(patch); if (use_patch(state, patch)) { - patch_stats(patch); + patch_stats(state, patch); *listp = patch; listp = &patch->next; } @@ -4563,7 +4564,7 @@ static int apply_patch(struct apply_state *state, build_fake_ancestor(list, state->fake_ancestor); if (state->diffstat) - stat_patch_list(list); + stat_patch_list(state, list); if (state->numstat) numstat_patch_list(state, list); -- cgit v1.2.1 From d7263d097c4510d864f8c9ff808bc9b377f0bf8f Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:20 +0200 Subject: builtin/apply: move 'state_linenr' global into 'struct apply_state' To libify the apply functionality the 'state_linenr' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 71 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 9e7d1810e5..dd56a8e93a 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -73,6 +73,9 @@ struct apply_state { struct string_list limit_by_name; int has_include; + /* Various "current state" */ + int linenr; /* current line number */ + /* * For "diff-stat" like behaviour, we keep track of the biggest change * we've seen, and the longest filename. That allows us to do simple @@ -149,13 +152,6 @@ static void set_default_whitespace_mode(struct apply_state *state) state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error); } -/* - * Various "current state", notably line numbers and what - * file (and how) we're patching right now.. The "is_xxxx" - * things are flags, where -1 means "don't know yet". - */ -static int state_linenr = 1; - /* * This represents one "hunk" from a patch, starting with * "@@ -oldpos,oldlines +newpos,newlines @@" marker. The @@ -932,7 +928,7 @@ static void parse_traditional_patch(struct apply_state *state, } } if (!name) - die(_("unable to find filename in patch at line %d"), state_linenr); + die(_("unable to find filename in patch at line %d"), state->linenr); } static int gitdiff_hdrend(struct apply_state *state, @@ -970,17 +966,17 @@ static void gitdiff_verify_name(struct apply_state *state, char *another; if (isnull) die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), - *name, state_linenr); + *name, state->linenr); another = find_name(state, line, NULL, state->p_value, TERM_TAB); if (!another || memcmp(another, *name, len + 1)) die((side == DIFF_NEW_NAME) ? _("git apply: bad git-diff - inconsistent new filename on line %d") : - _("git apply: bad git-diff - inconsistent old filename on line %d"), state_linenr); + _("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr); free(another); } else { /* expect "/dev/null" */ if (memcmp("/dev/null", line, 9) || line[9] != '\n') - die(_("git apply: bad git-diff - expected /dev/null on line %d"), state_linenr); + die(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr); } } @@ -1343,8 +1339,8 @@ static int parse_git_header(struct apply_state *state, line += len; size -= len; - state_linenr++; - for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state_linenr++) { + state->linenr++; + for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) { static const struct opentry { const char *str; int (*fn)(struct apply_state *, const char *, struct patch *); @@ -1515,7 +1511,7 @@ static int find_header(struct apply_state *state, patch->is_new = patch->is_delete = -1; patch->old_mode = patch->new_mode = 0; patch->old_name = patch->new_name = NULL; - for (offset = 0; size > 0; offset += len, size -= len, line += len, state_linenr++) { + for (offset = 0; size > 0; offset += len, size -= len, line += len, state->linenr++) { unsigned long nextlen; len = linelen(line, size); @@ -1536,7 +1532,7 @@ static int find_header(struct apply_state *state, if (parse_fragment_header(line, len, &dummy) < 0) continue; die(_("patch fragment without header at line %d: %.*s"), - state_linenr, (int)len-1, line); + state->linenr, (int)len-1, line); } if (size < len + 6) @@ -1557,13 +1553,13 @@ static int find_header(struct apply_state *state, "git diff header lacks filename information when removing " "%d leading pathname components (line %d)", state->p_value), - state->p_value, state_linenr); + state->p_value, state->linenr); patch->old_name = xstrdup(patch->def_name); patch->new_name = xstrdup(patch->def_name); } if (!patch->is_delete && !patch->new_name) die("git diff header lacks filename information " - "(line %d)", state_linenr); + "(line %d)", state->linenr); patch->is_toplevel_relative = 1; *hdrsize = git_hdr_len; return offset; @@ -1585,7 +1581,7 @@ static int find_header(struct apply_state *state, /* Ok, we'll consider it a patch */ parse_traditional_patch(state, line, line+len, patch); *hdrsize = len + nextlen; - state_linenr += 2; + state->linenr += 2; return offset; } return -1; @@ -1620,7 +1616,7 @@ static void check_whitespace(struct apply_state *state, { unsigned result = ws_check(line + 1, len - 1, ws_rule); - record_ws_error(state, result, line + 1, len - 2, state_linenr); + record_ws_error(state, result, line + 1, len - 2, state->linenr); } /* @@ -1653,11 +1649,11 @@ static int parse_fragment(struct apply_state *state, /* Parse the thing.. */ line += len; size -= len; - state_linenr++; + state->linenr++; added = deleted = 0; for (offset = len; 0 < size; - offset += len, size -= len, line += len, state_linenr++) { + offset += len, size -= len, line += len, state->linenr++) { if (!oldlines && !newlines) break; len = linelen(line, size); @@ -1756,10 +1752,10 @@ static int parse_single_patch(struct apply_state *state, int len; fragment = xcalloc(1, sizeof(*fragment)); - fragment->linenr = state_linenr; + fragment->linenr = state->linenr; len = parse_fragment(state, line, size, patch, fragment); if (len <= 0) - die(_("corrupt patch at line %d"), state_linenr); + die(_("corrupt patch at line %d"), state->linenr); fragment->patch = line; fragment->size = len; oldlines += fragment->oldlines; @@ -1845,7 +1841,8 @@ static char *inflate_it(const void *data, unsigned long size, * points at an allocated memory that the caller must free, so * it is marked as "->free_patch = 1". */ -static struct fragment *parse_binary_hunk(char **buf_p, +static struct fragment *parse_binary_hunk(struct apply_state *state, + char **buf_p, unsigned long *sz_p, int *status_p, int *used_p) @@ -1887,13 +1884,13 @@ static struct fragment *parse_binary_hunk(char **buf_p, else return NULL; - state_linenr++; + state->linenr++; buffer += llen; while (1) { int byte_length, max_byte_length, newsize; llen = linelen(buffer, size); used += llen; - state_linenr++; + state->linenr++; if (llen == 1) { /* consume the blank line */ buffer++; @@ -1947,7 +1944,7 @@ static struct fragment *parse_binary_hunk(char **buf_p, free(data); *status_p = -1; error(_("corrupt binary patch at line %d: %.*s"), - state_linenr-1, llen-1, buffer); + state->linenr-1, llen-1, buffer); return NULL; } @@ -1956,7 +1953,10 @@ static struct fragment *parse_binary_hunk(char **buf_p, * -1 in case of error, * the length of the parsed binary patch otherwise */ -static int parse_binary(char *buffer, unsigned long size, struct patch *patch) +static int parse_binary(struct apply_state *state, + char *buffer, + unsigned long size, + struct patch *patch) { /* * We have read "GIT binary patch\n"; what follows is a line @@ -1977,15 +1977,15 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch) int status; int used, used_1; - forward = parse_binary_hunk(&buffer, &size, &status, &used); + forward = parse_binary_hunk(state, &buffer, &size, &status, &used); if (!forward && !status) /* there has to be one hunk (forward hunk) */ - return error(_("unrecognized binary patch at line %d"), state_linenr-1); + return error(_("unrecognized binary patch at line %d"), state->linenr-1); if (status) /* otherwise we already gave an error message */ return status; - reverse = parse_binary_hunk(&buffer, &size, &status, &used_1); + reverse = parse_binary_hunk(state, &buffer, &size, &status, &used_1); if (reverse) used += used_1; else if (status) { @@ -2100,8 +2100,8 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si if (llen == sizeof(git_binary) - 1 && !memcmp(git_binary, buffer + hd, llen)) { int used; - state_linenr++; - used = parse_binary(buffer + hd + llen, + state->linenr++; + used = parse_binary(state, buffer + hd + llen, size - hd - llen, patch); if (used < 0) return -1; @@ -2121,7 +2121,7 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si int len = strlen(binhdr[i]); if (len < size - hd && !memcmp(binhdr[i], buffer + hd, len)) { - state_linenr++; + state->linenr++; patch->is_binary = 1; patchsize = llen; break; @@ -2135,7 +2135,7 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si */ if ((state->apply || state->check) && (!patch->is_binary && !metadata_changes(patch))) - die(_("patch with only garbage at line %d"), state_linenr); + die(_("patch with only garbage at line %d"), state->linenr); } return offset + hdrsize + patchsize; @@ -4654,6 +4654,7 @@ static void init_apply_state(struct apply_state *state, const char *prefix) state->squelch_whitespace_errors = 5; state->ws_error_action = warn_on_ws_error; state->ws_ignore_action = ignore_ws_none; + state->linenr = 1; strbuf_init(&state->root, 0); git_apply_config(); -- cgit v1.2.1 From 71dac5cef57daacb35a56b4b54e1a30bc6417968 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:21 +0200 Subject: builtin/apply: move 'fn_table' global into 'struct apply_state' To libify the apply functionality the 'fn_table' variable should not be static and global to the file. Let's move it into 'struct apply_state'. As fn_table is cleared at the end of apply_patch(), it is not necessary to clear it in clear_apply_state(). Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index dd56a8e93a..47622bee69 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -84,6 +84,12 @@ struct apply_state { int max_change; int max_len; + /* + * Records filenames that have been touched, in order to handle + * the case where more than one patches touch the same file. + */ + struct string_list fn_table; + /* These control whitespace errors */ enum ws_error_action ws_error_action; enum ws_ignore ws_ignore_action; @@ -271,13 +277,6 @@ struct image { struct line *line; }; -/* - * Records filenames that have been touched, in order to handle - * the case where more than one patches touch the same file. - */ - -static struct string_list fn_table; - static uint32_t hash_line(const char *cp, size_t len) { size_t i; @@ -3207,14 +3206,14 @@ static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf return read_blob_object(buf, ce->sha1, ce->ce_mode); } -static struct patch *in_fn_table(const char *name) +static struct patch *in_fn_table(struct apply_state *state, const char *name) { struct string_list_item *item; if (name == NULL) return NULL; - item = string_list_lookup(&fn_table, name); + item = string_list_lookup(&state->fn_table, name); if (item != NULL) return (struct patch *)item->util; @@ -3246,7 +3245,7 @@ static int was_deleted(struct patch *patch) return patch == PATH_WAS_DELETED; } -static void add_to_fn_table(struct patch *patch) +static void add_to_fn_table(struct apply_state *state, struct patch *patch) { struct string_list_item *item; @@ -3256,7 +3255,7 @@ static void add_to_fn_table(struct patch *patch) * file creations and copies */ if (patch->new_name != NULL) { - item = string_list_insert(&fn_table, patch->new_name); + item = string_list_insert(&state->fn_table, patch->new_name); item->util = patch; } @@ -3265,12 +3264,12 @@ static void add_to_fn_table(struct patch *patch) * later chunks shouldn't patch old names */ if ((patch->new_name == NULL) || (patch->is_rename)) { - item = string_list_insert(&fn_table, patch->old_name); + item = string_list_insert(&state->fn_table, patch->old_name); item->util = PATH_WAS_DELETED; } } -static void prepare_fn_table(struct patch *patch) +static void prepare_fn_table(struct apply_state *state, struct patch *patch) { /* * store information about incoming file deletion @@ -3278,7 +3277,7 @@ static void prepare_fn_table(struct patch *patch) while (patch) { if ((patch->new_name == NULL) || (patch->is_rename)) { struct string_list_item *item; - item = string_list_insert(&fn_table, patch->old_name); + item = string_list_insert(&state->fn_table, patch->old_name); item->util = PATH_TO_BE_DELETED; } patch = patch->next; @@ -3299,7 +3298,9 @@ static int checkout_target(struct index_state *istate, return 0; } -static struct patch *previous_patch(struct patch *patch, int *gone) +static struct patch *previous_patch(struct apply_state *state, + struct patch *patch, + int *gone) { struct patch *previous; @@ -3307,7 +3308,7 @@ static struct patch *previous_patch(struct patch *patch, int *gone) if (patch->is_copy || patch->is_rename) return NULL; /* "git" patches do not depend on the order */ - previous = in_fn_table(patch->old_name); + previous = in_fn_table(state, patch->old_name); if (!previous) return NULL; @@ -3376,7 +3377,7 @@ static int load_preimage(struct apply_state *state, struct patch *previous; int status; - previous = previous_patch(patch, &status); + previous = previous_patch(state, patch, &status); if (status) return error(_("path %s has been renamed/deleted"), patch->old_name); @@ -3572,7 +3573,7 @@ static int apply_data(struct apply_state *state, struct patch *patch, } patch->result = image.buf; patch->resultsize = image.len; - add_to_fn_table(patch); + add_to_fn_table(state, patch); free(image.line_allocated); if (0 < patch->is_delete && patch->resultsize) @@ -3606,7 +3607,7 @@ static int check_preimage(struct apply_state *state, return 0; assert(patch->is_new <= 0); - previous = previous_patch(patch, &status); + previous = previous_patch(state, patch, &status); if (status) return error(_("path %s has been renamed/deleted"), old_name); @@ -3852,7 +3853,7 @@ static int check_patch(struct apply_state *state, struct patch *patch) * B and rename from A to B is handled the same way by asking * was_deleted(). */ - if ((tpatch = in_fn_table(new_name)) && + if ((tpatch = in_fn_table(state, new_name)) && (was_deleted(tpatch) || to_be_deleted(tpatch))) ok_if_exists = 1; else @@ -3930,7 +3931,7 @@ static int check_patch_list(struct apply_state *state, struct patch *patch) int err = 0; prepare_symlink_changes(patch); - prepare_fn_table(patch); + prepare_fn_table(state, patch); while (patch) { if (state->apply_verbosely) say_patch_name(stderr, @@ -4574,7 +4575,7 @@ static int apply_patch(struct apply_state *state, free_patch_list(list); strbuf_release(&buf); - string_list_clear(&fn_table, 0); + string_list_clear(&state->fn_table, 0); return 0; } @@ -4668,6 +4669,8 @@ static void clear_apply_state(struct apply_state *state) { string_list_clear(&state->limit_by_name, 0); strbuf_release(&state->root); + + /* &state->fn_table is cleared at the end of apply_patch() */ } int cmd_apply(int argc, const char **argv, const char *prefix) -- cgit v1.2.1 From 2f63cea963667d17271d96196b6ef1605790032a Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:22 +0200 Subject: builtin/apply: move 'symlink_changes' global into 'struct apply_state' To libify the apply functionality the 'symlink_changes' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 50 ++++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 47622bee69..980bb34522 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -34,6 +34,20 @@ enum ws_ignore { ignore_ws_change }; +/* + * We need to keep track of how symlinks in the preimage are + * manipulated by the patches. A patch to add a/b/c where a/b + * is a symlink should not be allowed to affect the directory + * the symlink points at, but if the same patch removes a/b, + * it is perfectly fine, as the patch removes a/b to make room + * to create a directory a/b so that a/b/c can be created. + * + * See also "struct string_list symlink_changes" in "struct + * apply_state". + */ +#define SYMLINK_GOES_AWAY 01 +#define SYMLINK_IN_RESULT 02 + struct apply_state { const char *prefix; int prefix_length; @@ -75,6 +89,7 @@ struct apply_state { /* Various "current state" */ int linenr; /* current line number */ + struct string_list symlink_changes; /* we have to track symlinks */ /* * For "diff-stat" like behaviour, we keep track of the biggest change @@ -3702,52 +3717,42 @@ static int check_to_create(struct apply_state *state, return 0; } -/* - * We need to keep track of how symlinks in the preimage are - * manipulated by the patches. A patch to add a/b/c where a/b - * is a symlink should not be allowed to affect the directory - * the symlink points at, but if the same patch removes a/b, - * it is perfectly fine, as the patch removes a/b to make room - * to create a directory a/b so that a/b/c can be created. - */ -static struct string_list symlink_changes; -#define SYMLINK_GOES_AWAY 01 -#define SYMLINK_IN_RESULT 02 - -static uintptr_t register_symlink_changes(const char *path, uintptr_t what) +static uintptr_t register_symlink_changes(struct apply_state *state, + const char *path, + uintptr_t what) { struct string_list_item *ent; - ent = string_list_lookup(&symlink_changes, path); + ent = string_list_lookup(&state->symlink_changes, path); if (!ent) { - ent = string_list_insert(&symlink_changes, path); + ent = string_list_insert(&state->symlink_changes, path); ent->util = (void *)0; } ent->util = (void *)(what | ((uintptr_t)ent->util)); return (uintptr_t)ent->util; } -static uintptr_t check_symlink_changes(const char *path) +static uintptr_t check_symlink_changes(struct apply_state *state, const char *path) { struct string_list_item *ent; - ent = string_list_lookup(&symlink_changes, path); + ent = string_list_lookup(&state->symlink_changes, path); if (!ent) return 0; return (uintptr_t)ent->util; } -static void prepare_symlink_changes(struct patch *patch) +static void prepare_symlink_changes(struct apply_state *state, struct patch *patch) { for ( ; patch; patch = patch->next) { if ((patch->old_name && S_ISLNK(patch->old_mode)) && (patch->is_rename || patch->is_delete)) /* the symlink at patch->old_name is removed */ - register_symlink_changes(patch->old_name, SYMLINK_GOES_AWAY); + register_symlink_changes(state, patch->old_name, SYMLINK_GOES_AWAY); if (patch->new_name && S_ISLNK(patch->new_mode)) /* the symlink at patch->new_name is created or remains */ - register_symlink_changes(patch->new_name, SYMLINK_IN_RESULT); + register_symlink_changes(state, patch->new_name, SYMLINK_IN_RESULT); } } @@ -3761,7 +3766,7 @@ static int path_is_beyond_symlink_1(struct apply_state *state, struct strbuf *na if (!name->len) break; name->buf[name->len] = '\0'; - change = check_symlink_changes(name->buf); + change = check_symlink_changes(state, name->buf); if (change & SYMLINK_IN_RESULT) return 1; if (change & SYMLINK_GOES_AWAY) @@ -3930,7 +3935,7 @@ static int check_patch_list(struct apply_state *state, struct patch *patch) { int err = 0; - prepare_symlink_changes(patch); + prepare_symlink_changes(state, patch); prepare_fn_table(state, patch); while (patch) { if (state->apply_verbosely) @@ -4668,6 +4673,7 @@ static void init_apply_state(struct apply_state *state, const char *prefix) static void clear_apply_state(struct apply_state *state) { string_list_clear(&state->limit_by_name, 0); + string_list_clear(&state->symlink_changes, 0); strbuf_release(&state->root); /* &state->fn_table is cleared at the end of apply_patch() */ -- cgit v1.2.1 From c84a86c99526b88cfd7664888ecbfd28f35a025f Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:23 +0200 Subject: builtin/apply: move 'state' check into check_apply_state() To libify the apply functionality we should provide a function to check that the values in a 'struct apply_state' instance are coherent. Let's move the code to do that into a new check_apply_state() function. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 52 +++++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 980bb34522..8095026d33 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4679,11 +4679,38 @@ static void clear_apply_state(struct apply_state *state) /* &state->fn_table is cleared at the end of apply_patch() */ } +static void check_apply_state(struct apply_state *state, int force_apply) +{ + int is_not_gitdir = !startup_info->have_repository; + + if (state->apply_with_reject && state->threeway) + die("--reject and --3way cannot be used together."); + if (state->cached && state->threeway) + die("--cached and --3way cannot be used together."); + if (state->threeway) { + if (is_not_gitdir) + die(_("--3way outside a repository")); + state->check_index = 1; + } + if (state->apply_with_reject) + state->apply = state->apply_verbosely = 1; + if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor)) + state->apply = 0; + if (state->check_index && is_not_gitdir) + die(_("--index outside a repository")); + if (state->cached) { + if (is_not_gitdir) + die(_("--cached outside a repository")); + state->check_index = 1; + } + if (state->check_index) + state->unsafe_paths = 0; +} + int cmd_apply(int argc, const char **argv, const char *prefix) { int i; int errs = 0; - int is_not_gitdir = !startup_info->have_repository; int force_apply = 0; int options = 0; int read_stdin = 1; @@ -4763,28 +4790,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, state.prefix, builtin_apply_options, apply_usage, 0); - if (state.apply_with_reject && state.threeway) - die("--reject and --3way cannot be used together."); - if (state.cached && state.threeway) - die("--cached and --3way cannot be used together."); - if (state.threeway) { - if (is_not_gitdir) - die(_("--3way outside a repository")); - state.check_index = 1; - } - if (state.apply_with_reject) - state.apply = state.apply_verbosely = 1; - if (!force_apply && (state.diffstat || state.numstat || state.summary || state.check || state.fake_ancestor)) - state.apply = 0; - if (state.check_index && is_not_gitdir) - die(_("--index outside a repository")); - if (state.cached) { - if (is_not_gitdir) - die(_("--cached outside a repository")); - state.check_index = 1; - } - if (state.check_index) - state.unsafe_paths = 0; + check_apply_state(&state, force_apply); for (i = 0; i < argc; i++) { const char *arg = argv[i]; -- cgit v1.2.1 From 91b769c48f4cf46d9514770849a1bf9cb33430c3 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 24 May 2016 10:11:24 +0200 Subject: builtin/apply: move applying patches into apply_all_patches() To libify the apply functionality we should provide a function to apply many patches. Let's move the code to do that into a new apply_all_patches() function. Reviewed-by: Stefan Beller Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 129 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 71 insertions(+), 58 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 8095026d33..5027f1b504 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4707,13 +4707,80 @@ static void check_apply_state(struct apply_state *state, int force_apply) state->unsafe_paths = 0; } -int cmd_apply(int argc, const char **argv, const char *prefix) +static int apply_all_patches(struct apply_state *state, + int argc, + const char **argv, + int options) { int i; int errs = 0; + int read_stdin = 1; + + for (i = 0; i < argc; i++) { + const char *arg = argv[i]; + int fd; + + if (!strcmp(arg, "-")) { + errs |= apply_patch(state, 0, "", options); + read_stdin = 0; + continue; + } else if (0 < state->prefix_length) + arg = prefix_filename(state->prefix, + state->prefix_length, + arg); + + fd = open(arg, O_RDONLY); + if (fd < 0) + die_errno(_("can't open patch '%s'"), arg); + read_stdin = 0; + set_default_whitespace_mode(state); + errs |= apply_patch(state, fd, arg, options); + close(fd); + } + set_default_whitespace_mode(state); + if (read_stdin) + errs |= apply_patch(state, 0, "", options); + + if (state->whitespace_error) { + if (state->squelch_whitespace_errors && + state->squelch_whitespace_errors < state->whitespace_error) { + int squelched = + state->whitespace_error - state->squelch_whitespace_errors; + warning(Q_("squelched %d whitespace error", + "squelched %d whitespace errors", + squelched), + squelched); + } + if (state->ws_error_action == die_on_ws_error) + die(Q_("%d line adds whitespace errors.", + "%d lines add whitespace errors.", + state->whitespace_error), + state->whitespace_error); + if (state->applied_after_fixing_ws && state->apply) + warning("%d line%s applied after" + " fixing whitespace errors.", + state->applied_after_fixing_ws, + state->applied_after_fixing_ws == 1 ? "" : "s"); + else if (state->whitespace_error) + warning(Q_("%d line adds whitespace errors.", + "%d lines add whitespace errors.", + state->whitespace_error), + state->whitespace_error); + } + + if (state->update_index) { + if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) + die(_("Unable to write new index file")); + } + + return !!errs; +} + +int cmd_apply(int argc, const char **argv, const char *prefix) +{ int force_apply = 0; int options = 0; - int read_stdin = 1; + int ret; struct apply_state state; struct option builtin_apply_options[] = { @@ -4792,63 +4859,9 @@ int cmd_apply(int argc, const char **argv, const char *prefix) check_apply_state(&state, force_apply); - for (i = 0; i < argc; i++) { - const char *arg = argv[i]; - int fd; - - if (!strcmp(arg, "-")) { - errs |= apply_patch(&state, 0, "", options); - read_stdin = 0; - continue; - } else if (0 < state.prefix_length) - arg = prefix_filename(state.prefix, - state.prefix_length, - arg); - - fd = open(arg, O_RDONLY); - if (fd < 0) - die_errno(_("can't open patch '%s'"), arg); - read_stdin = 0; - set_default_whitespace_mode(&state); - errs |= apply_patch(&state, fd, arg, options); - close(fd); - } - set_default_whitespace_mode(&state); - if (read_stdin) - errs |= apply_patch(&state, 0, "", options); - if (state.whitespace_error) { - if (state.squelch_whitespace_errors && - state.squelch_whitespace_errors < state.whitespace_error) { - int squelched = - state.whitespace_error - state.squelch_whitespace_errors; - warning(Q_("squelched %d whitespace error", - "squelched %d whitespace errors", - squelched), - squelched); - } - if (state.ws_error_action == die_on_ws_error) - die(Q_("%d line adds whitespace errors.", - "%d lines add whitespace errors.", - state.whitespace_error), - state.whitespace_error); - if (state.applied_after_fixing_ws && state.apply) - warning("%d line%s applied after" - " fixing whitespace errors.", - state.applied_after_fixing_ws, - state.applied_after_fixing_ws == 1 ? "" : "s"); - else if (state.whitespace_error) - warning(Q_("%d line adds whitespace errors.", - "%d lines add whitespace errors.", - state.whitespace_error), - state.whitespace_error); - } - - if (state.update_index) { - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) - die(_("Unable to write new index file")); - } + ret = apply_all_patches(&state, argc, argv, options); clear_apply_state(&state); - return !!errs; + return ret; } -- cgit v1.2.1 From ed6e8038f98e7f15e571a9a291ee4acf438b4dc5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 2 Jun 2016 14:09:22 -0700 Subject: pathspec: rename free_pathspec() to clear_pathspec() The function takes a pointer to a pathspec structure, and releases the resources held by it, but does not free() the structure itself. Such a function should be called "clear", not "free". Signed-off-by: Junio C Hamano --- archive.c | 2 +- builtin/blame.c | 6 +++--- builtin/reset.c | 2 +- builtin/update-index.c | 2 +- combine-diff.c | 2 +- notes-merge.c | 4 ++-- pathspec.c | 2 +- pathspec.h | 4 ++-- revision.c | 2 +- tree-diff.c | 4 ++-- 10 files changed, 15 insertions(+), 15 deletions(-) diff --git a/archive.c b/archive.c index 5d735ae603..42df9748d6 100644 --- a/archive.c +++ b/archive.c @@ -322,7 +322,7 @@ static int path_exists(struct tree *tree, const char *path) pathspec.recursive = 1; ret = read_tree_recursive(tree, "", 0, 0, &pathspec, reject_entry, &pathspec); - free_pathspec(&pathspec); + clear_pathspec(&pathspec); return ret != 0; } diff --git a/builtin/blame.c b/builtin/blame.c index 21f42b0b62..759d84affb 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -609,7 +609,7 @@ static struct origin *find_origin(struct scoreboard *sb, } } diff_flush(&diff_opts); - free_pathspec(&diff_opts.pathspec); + clear_pathspec(&diff_opts.pathspec); return porigin; } @@ -651,7 +651,7 @@ static struct origin *find_rename(struct scoreboard *sb, } } diff_flush(&diff_opts); - free_pathspec(&diff_opts.pathspec); + clear_pathspec(&diff_opts.pathspec); return porigin; } @@ -1343,7 +1343,7 @@ static void find_copy_in_parent(struct scoreboard *sb, } while (unblamed); target->suspects = reverse_blame(leftover, NULL); diff_flush(&diff_opts); - free_pathspec(&diff_opts.pathspec); + clear_pathspec(&diff_opts.pathspec); } /* diff --git a/builtin/reset.c b/builtin/reset.c index 092c3a5399..acd6278868 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -158,7 +158,7 @@ static int read_from_tree(const struct pathspec *pathspec, return 1; diffcore_std(&opt); diff_flush(&opt); - free_pathspec(&opt.pathspec); + clear_pathspec(&opt.pathspec); return 0; } diff --git a/builtin/update-index.c b/builtin/update-index.c index b8b8522249..6cdfd5f730 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -759,7 +759,7 @@ static int do_reupdate(int ac, const char **av, if (save_nr != active_nr) goto redo; } - free_pathspec(&pathspec); + clear_pathspec(&pathspec); return 0; } diff --git a/combine-diff.c b/combine-diff.c index 8f2313d502..5920df8b8d 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -1525,7 +1525,7 @@ void diff_tree_combined(const unsigned char *sha1, free(tmp); } - free_pathspec(&diffopts.pathspec); + clear_pathspec(&diffopts.pathspec); } void diff_tree_combined_merge(const struct commit *commit, int dense, diff --git a/notes-merge.c b/notes-merge.c index 34bfac0c68..b7814c9b64 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -170,7 +170,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o, sha1_to_hex(mp->remote)); } diff_flush(&opt); - free_pathspec(&opt.pathspec); + clear_pathspec(&opt.pathspec); *num_changes = len; return changes; @@ -256,7 +256,7 @@ static void diff_tree_local(struct notes_merge_options *o, sha1_to_hex(mp->local)); } diff_flush(&opt); - free_pathspec(&opt.pathspec); + clear_pathspec(&opt.pathspec); } static void check_notes_merge_worktree(struct notes_merge_options *o) diff --git a/pathspec.c b/pathspec.c index c9e9b6c077..24e0dd5232 100644 --- a/pathspec.c +++ b/pathspec.c @@ -489,7 +489,7 @@ void copy_pathspec(struct pathspec *dst, const struct pathspec *src) sizeof(struct pathspec_item) * dst->nr); } -void free_pathspec(struct pathspec *pathspec) +void clear_pathspec(struct pathspec *pathspec) { free(pathspec->items); pathspec->items = NULL; diff --git a/pathspec.h b/pathspec.h index 0c1126264a..4a80f6fc96 100644 --- a/pathspec.h +++ b/pathspec.h @@ -19,7 +19,7 @@ #define PATHSPEC_ONESTAR 1 /* the pathspec pattern satisfies GFNM_ONESTAR */ struct pathspec { - const char **_raw; /* get_pathspec() result, not freed by free_pathspec() */ + const char **_raw; /* get_pathspec() result, not freed by clear_pathspec() */ int nr; unsigned int has_wildcard:1; unsigned int recursive:1; @@ -74,7 +74,7 @@ extern void parse_pathspec(struct pathspec *pathspec, const char *prefix, const char **args); extern void copy_pathspec(struct pathspec *dst, const struct pathspec *src); -extern void free_pathspec(struct pathspec *); +extern void clear_pathspec(struct pathspec *); static inline int ps_strncmp(const struct pathspec_item *item, const char *s1, const char *s2, size_t n) diff --git a/revision.c b/revision.c index d30d1c4f80..2f60062876 100644 --- a/revision.c +++ b/revision.c @@ -1425,7 +1425,7 @@ static void prepare_show_merge(struct rev_info *revs) ce_same_name(ce, active_cache[i+1])) i++; } - free_pathspec(&revs->prune_data); + clear_pathspec(&revs->prune_data); parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_PREFER_FULL | PATHSPEC_LITERAL_PATH, "", prune); revs->limited = 1; diff --git a/tree-diff.c b/tree-diff.c index ff4e0d3cb6..edb4de9b72 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -607,7 +607,7 @@ static void try_to_follow_renames(const unsigned char *old, const unsigned char diff_setup_done(&diff_opts); ll_diff_tree_sha1(old, new, base, &diff_opts); diffcore_std(&diff_opts); - free_pathspec(&diff_opts.pathspec); + clear_pathspec(&diff_opts.pathspec); /* Go through the new set of filepairing, and see if we find a more interesting one */ opt->found_follow = 0; @@ -630,7 +630,7 @@ static void try_to_follow_renames(const unsigned char *old, const unsigned char /* Update the path we use from now on.. */ path[0] = p->one->path; path[1] = NULL; - free_pathspec(&opt->pathspec); + clear_pathspec(&opt->pathspec); parse_pathspec(&opt->pathspec, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_LITERAL_PATH, "", path); -- cgit v1.2.1 From 58461bdf15a66f428f5ca6042cafbcc64c82c64d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 2 Jun 2016 15:06:55 -0700 Subject: t1308: do not get fooled by symbolic links to the source tree When your $PWD does not match $(/bin/pwd), e.g. you have your copy of the git source tree in one place, point it with a symbolic link, and then "cd" to that symbolic link before running 'make test', one of the tests in t1308 expects that the per-user configuration was reported to have been read from the true path (i.e. relative to the target of such a symbolic link), but the test-config program reports a path relative to $PWD (i.e. the symbolic link). Instead, expect a path relative to $HOME (aka $TRASH_DIRECTORY), as per-user configuration is read from $HOME/.gitconfig and the test framework sets these shell variables up in such a way to avoid this problem. Signed-off-by: Junio C Hamano --- t/t1308-config-set.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index 065d5ebb09..cf716b469f 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -237,7 +237,7 @@ test_expect_success 'iteration shows correct origins' ' key=foo.bar value=from-home origin=file - name=$(pwd)/.gitconfig + name=$HOME/.gitconfig scope=global key=foo.bar -- cgit v1.2.1 From 20b20a22f8f7c1420e259c97ef790cb93091f475 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 18 May 2016 18:45:37 -0400 Subject: upload-pack: provide a hook for running pack-objects When upload-pack serves a client request, it turns to pack-objects to do the heavy lifting of creating a packfile. There's no easy way to intercept the call to pack-objects, but there are a few good reasons to want to do so: 1. If you're debugging a client or server issue with fetching, you may want to store a copy of the generated packfile. 2. If you're gathering data from real-world fetches for performance analysis or debugging, storing a copy of the arguments and stdin lets you replay the pack generation at your leisure. 3. You may want to insert a caching layer around pack-objects; it is the most CPU- and memory-intensive part of serving a fetch, and its output is a pure function[1] of its input, making it an ideal place to consolidate identical requests. This patch adds a simple "hook" interface to intercept calls to pack-objects. The new test demonstrates how it can be used for debugging (using it for caching is a straightforward extension; the tricky part is writing the actual caching layer). This hook is unlike the normal hook scripts found in the "hooks/" directory of a repository. Because we promise that upload-pack is safe to run in an untrusted repository, we cannot execute arbitrary code or commands found in the repository (neither in hooks/, nor in the config). So instead, this hook is triggered from a config variable that is explicitly ignored in the per-repo config. The config variable holds the actual shell command to run as the hook. Another approach would be to simply treat it as a boolean: "should I respect the upload-pack hooks in this repo?", and then run the script from "hooks/" as we usually do. However, that isn't as flexible; there's no way to run a hook approved by the site administrator (e.g., in "/etc/gitconfig") on a repository whose contents are not trusted. The approach taken by this patch is more fine-grained, if a little less conventional for git hooks (it does behave similar to other configured commands like diff.external, etc). [1] Pack-objects isn't _actually_ a pure function. Its output depends on the exact packing of the object database, and if multi-threading is used for delta compression, can even differ racily. But for the purposes of caching, that's OK; of the many possible outputs for a given input, it is sufficient only that we output one of them. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/config.txt | 15 +++++++++++ t/t5544-pack-objects-hook.sh | 62 ++++++++++++++++++++++++++++++++++++++++++++ upload-pack.c | 13 +++++++++- 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100755 t/t5544-pack-objects-hook.sh diff --git a/Documentation/config.txt b/Documentation/config.txt index 53f00dbc26..a7abcbeff3 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -2880,6 +2880,21 @@ uploadpack.keepAlive:: `uploadpack.keepAlive` seconds. Setting this option to 0 disables keepalive packets entirely. The default is 5 seconds. +uploadpack.packObjectsHook:: + If this option is set, when `upload-pack` would run + `git pack-objects` to create a packfile for a client, it will + run this shell command instead. The `pack-objects` command and + arguments it _would_ have run (including the `git pack-objects` + at the beginning) are appended to the shell command. The stdin + and stdout of the hook are treated as if `pack-objects` itself + was run. I.e., `upload-pack` will feed input intended for + `pack-objects` to the hook, and expects a completed packfile on + stdout. ++ +Note that this configuration variable is ignored if it is seen in the +repository-level config (this is a safety measure against fetching from +untrusted repositories). + url..insteadOf:: Any URL that starts with this value will be rewritten to start, instead, with . In cases where some site serves a diff --git a/t/t5544-pack-objects-hook.sh b/t/t5544-pack-objects-hook.sh new file mode 100755 index 0000000000..4357af1525 --- /dev/null +++ b/t/t5544-pack-objects-hook.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +test_description='test custom script in place of pack-objects' +. ./test-lib.sh + +test_expect_success 'create some history to fetch' ' + test_commit one && + test_commit two +' + +test_expect_success 'create debugging hook script' ' + write_script .git/hook <<-\EOF + echo >&2 "hook running" + echo "$*" >hook.args + cat >hook.stdin + "$@" hook.stdout + cat hook.stdout + EOF +' + +clear_hook_results () { + rm -rf .git/hook.* dst.git +} + +test_expect_success 'hook runs via global config' ' + clear_hook_results && + test_config_global uploadpack.packObjectsHook ./hook && + git clone --no-local . dst.git 2>stderr && + grep "hook running" stderr +' + +test_expect_success 'hook outputs are sane' ' + # check that we recorded a usable pack + git index-pack --stdin <.git/hook.stdout && + + # check that we recorded args and stdin. We do not check + # the full argument list or the exact pack contents, as it would make + # the test brittle. So just sanity check that we could replay + # the packing procedure. + grep "^git" .git/hook.args && + $(cat .git/hook.args) <.git/hook.stdin >replay +' + +test_expect_success 'hook runs from -c config' ' + clear_hook_results && + git clone --no-local \ + -u "git -c uploadpack.packObjectsHook=./hook upload-pack" \ + . dst.git 2>stderr && + grep "hook running" stderr +' + +test_expect_success 'hook does not run from repo config' ' + clear_hook_results && + test_config uploadpack.packObjectsHook "./hook" && + git clone --no-local . dst.git 2>stderr && + ! grep "hook running" stderr && + test_path_is_missing .git/hook.args && + test_path_is_missing .git/hook.stdin && + test_path_is_missing .git/hook.stdout +' + +test_done diff --git a/upload-pack.c b/upload-pack.c index f19444df7b..8979be6394 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -52,6 +52,7 @@ static int keepalive = 5; static int use_sideband; static int advertise_refs; static int stateless_rpc; +static const char *pack_objects_hook; static void reset_timeout(void) { @@ -93,6 +94,14 @@ static void create_pack_file(void) int i; FILE *pipe_fd; + if (!pack_objects_hook) + pack_objects.git_cmd = 1; + else { + argv_array_push(&pack_objects.args, pack_objects_hook); + argv_array_push(&pack_objects.args, "git"); + pack_objects.use_shell = 1; + } + if (shallow_nr) { argv_array_push(&pack_objects.args, "--shallow-file"); argv_array_push(&pack_objects.args, ""); @@ -115,7 +124,6 @@ static void create_pack_file(void) pack_objects.in = -1; pack_objects.out = -1; pack_objects.err = -1; - pack_objects.git_cmd = 1; if (start_command(&pack_objects)) die("git upload-pack: unable to fork git-pack-objects"); @@ -812,6 +820,9 @@ static int upload_pack_config(const char *var, const char *value, void *unused) keepalive = git_config_int(var, value); if (!keepalive) keepalive = -1; + } else if (current_config_scope() != CONFIG_SCOPE_REPO) { + if (!strcmp("uploadpack.packobjectshook", var)) + return git_config_string(&pack_objects_hook, var, value); } return parse_hide_refs_config(var, value, "uploadpack"); } -- cgit v1.2.1 From 8f31fac365c312aa9109a7a1fc1014e56ed473d2 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 3 Jun 2016 18:58:51 +0200 Subject: builtin/apply: add 'lock_file' pointer into 'struct apply_state' We cannot have a 'struct lock_file' allocated on the stack, as lockfile.c keeps a linked list of all created lock_file structures. Also 'struct apply_state' users might later want the same 'struct lock_file' instance to be reused by different series of calls to the apply api. So let's add a 'struct lock_file *lock_file' pointer into 'struct apply_state' and have the user of 'struct apply_state' allocate memory for the actual 'struct lock_file' instance. Let's also add an argument to init_apply_state(), so that the caller can easily supply a pointer to the allocated instance. Helped-by: Eric Sunshine Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 5027f1b504..cc635ebd1a 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -52,6 +52,12 @@ struct apply_state { const char *prefix; int prefix_length; + /* + * Since lockfile.c keeps a linked list of all created + * lock_file structures, it isn't safe to free(lock_file). + */ + struct lock_file *lock_file; + /* These control what gets looked at and modified */ int apply; /* this is not a dry-run */ int cached; /* apply to the index only */ @@ -4547,7 +4553,7 @@ static int apply_patch(struct apply_state *state, state->update_index = state->check_index && state->apply; if (state->update_index && newfd < 0) - newfd = hold_locked_index(&lock_file, 1); + newfd = hold_locked_index(state->lock_file, 1); if (state->check_index) { if (read_cache() < 0) @@ -4648,11 +4654,14 @@ static int option_parse_directory(const struct option *opt, return 0; } -static void init_apply_state(struct apply_state *state, const char *prefix) +static void init_apply_state(struct apply_state *state, + const char *prefix, + struct lock_file *lock_file) { memset(state, 0, sizeof(*state)); state->prefix = prefix; state->prefix_length = state->prefix ? strlen(state->prefix) : 0; + state->lock_file = lock_file; state->apply = 1; state->line_termination = '\n'; state->p_value = 1; @@ -4705,6 +4714,8 @@ static void check_apply_state(struct apply_state *state, int force_apply) } if (state->check_index) state->unsafe_paths = 0; + if (!state->lock_file) + die("BUG: state->lock_file should not be NULL"); } static int apply_all_patches(struct apply_state *state, @@ -4769,7 +4780,7 @@ static int apply_all_patches(struct apply_state *state, } if (state->update_index) { - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) + if (write_locked_index(&the_index, state->lock_file, COMMIT_LOCK)) die(_("Unable to write new index file")); } @@ -4852,7 +4863,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix) OPT_END() }; - init_apply_state(&state, prefix); + init_apply_state(&state, prefix, &lock_file); argc = parse_options(argc, argv, state.prefix, builtin_apply_options, apply_usage, 0); -- cgit v1.2.1 From a1bc3dd46433c6e7e6f1ca5f3bd5429858a60078 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 3 Jun 2016 18:58:52 +0200 Subject: builtin/apply: move 'newfd' global into 'struct apply_state' To libify the apply functionality the 'newfd' variable should not be static and global to the file. Let's move it into 'struct apply_state'. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin/apply.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index cc635ebd1a..7338ee6b68 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -57,6 +57,7 @@ struct apply_state { * lock_file structures, it isn't safe to free(lock_file). */ struct lock_file *lock_file; + int newfd; /* These control what gets looked at and modified */ int apply; /* this is not a dry-run */ @@ -120,8 +121,6 @@ struct apply_state { int applied_after_fixing_ws; }; -static int newfd = -1; - static const char * const apply_usage[] = { N_("git apply [] [...]"), NULL @@ -4552,8 +4551,8 @@ static int apply_patch(struct apply_state *state, state->apply = 0; state->update_index = state->check_index && state->apply; - if (state->update_index && newfd < 0) - newfd = hold_locked_index(state->lock_file, 1); + if (state->update_index && state->newfd < 0) + state->newfd = hold_locked_index(state->lock_file, 1); if (state->check_index) { if (read_cache() < 0) @@ -4662,6 +4661,7 @@ static void init_apply_state(struct apply_state *state, state->prefix = prefix; state->prefix_length = state->prefix ? strlen(state->prefix) : 0; state->lock_file = lock_file; + state->newfd = -1; state->apply = 1; state->line_termination = '\n'; state->p_value = 1; @@ -4782,6 +4782,7 @@ static int apply_all_patches(struct apply_state *state, if (state->update_index) { if (write_locked_index(&the_index, state->lock_file, COMMIT_LOCK)) die(_("Unable to write new index file")); + state->newfd = -1; } return !!errs; -- cgit v1.2.1 From 0719f3eecd1234f6331cab980088239207e93335 Mon Sep 17 00:00:00 2001 From: William Duclot Date: Fri, 3 Jun 2016 14:32:26 +0200 Subject: userdiff: add built-in pattern for CSS CSS is widely used, motivating it being included as a built-in pattern. It must be noted that the word_regex for CSS (i.e. the regex defining what is a word in the language) does not consider '.' and '#' characters (in CSS selectors) to be part of the word. This behavior is documented by the test t/t4018/css-rule. The logic behind this behavior is the following: identifiers in CSS selectors are identifiers in a HTML/XML document. Therefore, the '.'/'#' character are not part of the identifier, but an indicator of the nature of the identifier in HTML/XML (class or id). Diffing ".class1" and ".class2" must show that the class name is changed, but we still are selecting a class. Logic behind the "pattern" regex is: 1. reject lines ending with a colon/semicolon (properties) 2. if a line begins with a name in column 1, pick the whole line Credits to Johannes Sixt (j6t@kdbg.org) for the pattern regex and most of the tests. Signed-off-by: William Duclot Signed-off-by: Matthieu Moy Reviewed-by: Johannes Sixt Signed-off-by: Junio C Hamano --- Documentation/gitattributes.txt | 2 ++ t/t4018-diff-funcname.sh | 1 + t/t4018/css-brace-in-col-1 | 5 +++++ t/t4018/css-colon-eol | 4 ++++ t/t4018/css-colon-selector | 5 +++++ t/t4018/css-common | 4 ++++ t/t4018/css-long-selector-list | 6 ++++++ t/t4018/css-prop-sans-indent | 5 +++++ t/t4018/css-short-selector-list | 4 ++++ t/t4018/css-trailing-space | 5 +++++ t/t4034-diff-words.sh | 1 + t/t4034/css/expect | 16 ++++++++++++++++ t/t4034/css/post | 10 ++++++++++ t/t4034/css/pre | 10 ++++++++++ userdiff.c | 12 ++++++++++++ 15 files changed, 90 insertions(+) create mode 100644 t/t4018/css-brace-in-col-1 create mode 100644 t/t4018/css-colon-eol create mode 100644 t/t4018/css-colon-selector create mode 100644 t/t4018/css-common create mode 100644 t/t4018/css-long-selector-list create mode 100644 t/t4018/css-prop-sans-indent create mode 100644 t/t4018/css-short-selector-list create mode 100644 t/t4018/css-trailing-space create mode 100644 t/t4034/css/expect create mode 100644 t/t4034/css/post create mode 100644 t/t4034/css/pre diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index e3b1de8033..8882a3e914 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -525,6 +525,8 @@ patterns are available: - `csharp` suitable for source code in the C# language. +- `css` suitable for cascading style sheets. + - `fortran` suitable for source code in the Fortran language. - `fountain` suitable for Fountain documents. diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh index 67373dc44e..1795ffc3aa 100755 --- a/t/t4018-diff-funcname.sh +++ b/t/t4018-diff-funcname.sh @@ -30,6 +30,7 @@ diffpatterns=" bibtex cpp csharp + css fortran fountain html diff --git a/t/t4018/css-brace-in-col-1 b/t/t4018/css-brace-in-col-1 new file mode 100644 index 0000000000..7831577506 --- /dev/null +++ b/t/t4018/css-brace-in-col-1 @@ -0,0 +1,5 @@ +RIGHT label.control-label +{ + margin-top: 10px!important; + border : 10px ChangeMe #C6C6C6; +} diff --git a/t/t4018/css-colon-eol b/t/t4018/css-colon-eol new file mode 100644 index 0000000000..5a30553d29 --- /dev/null +++ b/t/t4018/css-colon-eol @@ -0,0 +1,4 @@ +RIGHT h1 { +color: +ChangeMe; +} diff --git a/t/t4018/css-colon-selector b/t/t4018/css-colon-selector new file mode 100644 index 0000000000..c6d71fb42d --- /dev/null +++ b/t/t4018/css-colon-selector @@ -0,0 +1,5 @@ +RIGHT a:hover { + margin-top: + 10px!important; + border : 10px ChangeMe #C6C6C6; +} diff --git a/t/t4018/css-common b/t/t4018/css-common new file mode 100644 index 0000000000..84ed754b33 --- /dev/null +++ b/t/t4018/css-common @@ -0,0 +1,4 @@ +RIGHT label.control-label { + margin-top: 10px!important; + border : 10px ChangeMe #C6C6C6; +} diff --git a/t/t4018/css-long-selector-list b/t/t4018/css-long-selector-list new file mode 100644 index 0000000000..7ccd25d9ed --- /dev/null +++ b/t/t4018/css-long-selector-list @@ -0,0 +1,6 @@ +p.header, +label.control-label, +div ul#RIGHT { + margin-top: 10px!important; + border : 10px ChangeMe #C6C6C6; +} diff --git a/t/t4018/css-prop-sans-indent b/t/t4018/css-prop-sans-indent new file mode 100644 index 0000000000..a9e3c86b3c --- /dev/null +++ b/t/t4018/css-prop-sans-indent @@ -0,0 +1,5 @@ +RIGHT, label.control-label { +margin-top: 10px!important; +padding: 0; +border : 10px ChangeMe #C6C6C6; +} diff --git a/t/t4018/css-short-selector-list b/t/t4018/css-short-selector-list new file mode 100644 index 0000000000..6a0bdee336 --- /dev/null +++ b/t/t4018/css-short-selector-list @@ -0,0 +1,4 @@ +label.control, div ul#RIGHT { + margin-top: 10px!important; + border : 10px ChangeMe #C6C6C6; +} diff --git a/t/t4018/css-trailing-space b/t/t4018/css-trailing-space new file mode 100644 index 0000000000..32b5606c70 --- /dev/null +++ b/t/t4018/css-trailing-space @@ -0,0 +1,5 @@ +RIGHT label.control-label { + margin:10px; + padding:10px; + border : 10px ChangeMe #C6C6C6; +} diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh index f2f55fc51c..912df91226 100755 --- a/t/t4034-diff-words.sh +++ b/t/t4034-diff-words.sh @@ -302,6 +302,7 @@ test_language_driver ada test_language_driver bibtex test_language_driver cpp test_language_driver csharp +test_language_driver css test_language_driver fortran test_language_driver html test_language_driver java diff --git a/t/t4034/css/expect b/t/t4034/css/expect new file mode 100644 index 0000000000..ed10393bda --- /dev/null +++ b/t/t4034/css/expect @@ -0,0 +1,16 @@ +diff --git a/pre b/post +index b8ae0bb..fe500b7 100644 +--- a/pre ++++ b/post +@@ -1,10 +1,10 @@ +.class-formother-form label.control-label { + margin-top: 1015px!important; + border : 10px dasheddotted #C6C6C6; +} +#CCCCCC#CCCCCB +10em +padding-bottommargin-left +150pxem +10px +!important +divli.class#id diff --git a/t/t4034/css/post b/t/t4034/css/post new file mode 100644 index 0000000000..fe500b7a4f --- /dev/null +++ b/t/t4034/css/post @@ -0,0 +1,10 @@ +.other-form label.control-label { + margin-top: 15px!important; + border : 10px dotted #C6C6C6; +} +#CCCCCB +10em +margin-left +150em +10px +li.class#id diff --git a/t/t4034/css/pre b/t/t4034/css/pre new file mode 100644 index 0000000000..b8ae0bb48f --- /dev/null +++ b/t/t4034/css/pre @@ -0,0 +1,10 @@ +.class-form label.control-label { + margin-top: 10px!important; + border : 10px dashed #C6C6C6; +} +#CCCCCC +10em +padding-bottom +150px +10px!important +div.class#id diff --git a/userdiff.c b/userdiff.c index 6bf2505994..2125d6da26 100644 --- a/userdiff.c +++ b/userdiff.c @@ -148,6 +148,18 @@ PATTERNS("csharp", "[a-zA-Z_][a-zA-Z0-9_]*" "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), +IPATTERN("css", + "![:;][[:space:]]*$\n" + "^[_a-z0-9].*$", + /* -- */ + /* + * This regex comes from W3C CSS specs. Should theoretically also + * allow ISO 10646 characters U+00A0 and higher, + * but they are not handled in this regex. + */ + "-?[_a-zA-Z][-_a-zA-Z0-9]*" /* identifiers */ + "|-?[0-9]+|\\#[0-9a-fA-F]+" /* numbers */ +), { "default", NULL, -1, { NULL, 0 } }, }; #undef PATTERNS -- cgit v1.2.1 From 6835314459794831a1b88bed56549653710e910c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Fri, 3 Jun 2016 19:19:39 +0700 Subject: worktree.c: add find_worktree() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far we haven't needed to identify an existing worktree from command line. Future commands such as lock or move will need it. The current implementation identifies worktrees by path (*). In future, the function could learn to identify by $(basename $path) or tags... (*) We could probably go cheaper with comparing inode number (and probably more reliable than paths when unicode enters the game). But not all systems have good inode that so let's stick to something simple for now. Helped-by: Eric Sunshine Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- worktree.c | 15 +++++++++++++++ worktree.h | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/worktree.c b/worktree.c index f4a4f38092..0782e00983 100644 --- a/worktree.c +++ b/worktree.c @@ -214,6 +214,21 @@ const char *get_worktree_git_dir(const struct worktree *wt) return git_common_path("worktrees/%s", wt->id); } +struct worktree *find_worktree(struct worktree **list, + const char *prefix, + const char *arg) +{ + char *path; + + arg = prefix_filename(prefix, strlen(prefix), arg); + path = xstrdup(real_path(arg)); + for (; *list; list++) + if (!fspathcmp(path, real_path((*list)->path))) + break; + free(path); + return *list; +} + int is_worktree_being_rebased(const struct worktree *wt, const char *target) { diff --git a/worktree.h b/worktree.h index 13949093cc..7ad15da0dc 100644 --- a/worktree.h +++ b/worktree.h @@ -29,6 +29,14 @@ extern struct worktree **get_worktrees(void); */ extern const char *get_worktree_git_dir(const struct worktree *wt); +/* + * Search a worktree that can be unambiguously identified by + * "arg". "prefix" must not be NULL. + */ +extern struct worktree *find_worktree(struct worktree **list, + const char *prefix, + const char *arg); + /* * Free up the memory for worktree(s) */ -- cgit v1.2.1 From 984ad9e56c9d884f7f1aa7d6b2052f3fb082ee5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Fri, 3 Jun 2016 19:19:40 +0700 Subject: worktree.c: add is_main_worktree() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Main worktree _is_ different. You can lock (*) a linked worktree but not the main one, for example. Provide an API for checking that. (*) Add the file $GIT_DIR/worktrees/xxx/locked to avoid worktree xxx from being removed or moved. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- worktree.c | 5 +++++ worktree.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/worktree.c b/worktree.c index 0782e00983..12a766a38d 100644 --- a/worktree.c +++ b/worktree.c @@ -229,6 +229,11 @@ struct worktree *find_worktree(struct worktree **list, return *list; } +int is_main_worktree(const struct worktree *wt) +{ + return !wt->id; +} + int is_worktree_being_rebased(const struct worktree *wt, const char *target) { diff --git a/worktree.h b/worktree.h index 7ad15da0dc..e1c4715238 100644 --- a/worktree.h +++ b/worktree.h @@ -37,6 +37,11 @@ extern struct worktree *find_worktree(struct worktree **list, const char *prefix, const char *arg); +/* + * Return true if the given worktree is the main one. + */ +extern int is_main_worktree(const struct worktree *wt); + /* * Free up the memory for worktree(s) */ -- cgit v1.2.1 From 860a2ebecd26563e6ac0f89c90052b41a17d1703 Mon Sep 17 00:00:00 2001 From: Lukas Fleischer Date: Sun, 5 Jun 2016 11:36:38 +0200 Subject: receive-pack: send auto-gc output over sideband 2 Redirect auto-gc output to the sideband such that it is visible to all clients. As a side effect, all auto-gc error messages are now prefixed with "remote: " before being printed to stderr on the client-side which makes it easier to understand that those error messages originate from the server. Signed-off-by: Lukas Fleischer Signed-off-by: Junio C Hamano --- builtin/receive-pack.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index c8e32b297c..4abfa38a12 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1788,9 +1788,20 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) const char *argv_gc_auto[] = { "gc", "--auto", "--quiet", NULL, }; - int opt = RUN_GIT_CMD | RUN_COMMAND_STDOUT_TO_STDERR; + struct child_process proc = CHILD_PROCESS_INIT; + + proc.no_stdin = 1; + proc.stdout_to_stderr = 1; + proc.err = use_sideband ? -1 : 0; + proc.git_cmd = 1; + proc.argv = argv_gc_auto; + close_all_packs(); - run_command_v_opt(argv_gc_auto, opt); + if (!start_command(&proc)) { + if (use_sideband) + copy_to_sideband(proc.err, -1, NULL); + finish_command(&proc); + } } if (auto_update_server_info) update_server_info(0); -- cgit v1.2.1 From 9f23e040615857e3909db51a5420f338c9831b5a Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 5 Jun 2016 04:46:39 +0000 Subject: pretty: support "mboxrd" output format This output format prevents format-patch output from breaking readers if somebody copy+pasted an mbox into a commit message. Unlike the traditional "mboxo" format, "mboxrd" is designed to be fully-reversible. "mboxrd" also gracefully degrades to showing extra ">" in existing "mboxo" readers. This degradation is preferable to breaking message splitting completely, a problem I've seen in "mboxcl" due to having multiple, non-existent, or inaccurate Content-Length headers. "mboxcl2" is a non-starter since it's inherits the problems of "mboxcl" while being completely incompatible with existing tooling based around mailsplit. ref: http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/mail-mbox-formats.html Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- builtin/log.c | 2 +- commit.h | 6 ++++++ log-tree.c | 4 ++-- pretty.c | 33 +++++++++++++++++++++++++-------- t/t4014-format-patch.sh | 41 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 11 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index 099f4f7be9..6d6f368175 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -953,7 +953,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, struct pretty_print_context pp = {0}; struct commit *head = list[0]; - if (rev->commit_format != CMIT_FMT_EMAIL) + if (!cmit_fmt_is_mail(rev->commit_format)) die(_("Cover letter needs email format")); committer = git_committer_info(0); diff --git a/commit.h b/commit.h index b06db4d5d9..1e04d3ae07 100644 --- a/commit.h +++ b/commit.h @@ -131,11 +131,17 @@ enum cmit_fmt { CMIT_FMT_FULLER, CMIT_FMT_ONELINE, CMIT_FMT_EMAIL, + CMIT_FMT_MBOXRD, CMIT_FMT_USERFORMAT, CMIT_FMT_UNSPECIFIED }; +static inline int cmit_fmt_is_mail(enum cmit_fmt fmt) +{ + return (fmt == CMIT_FMT_EMAIL || fmt == CMIT_FMT_MBOXRD); +} + struct pretty_print_context { /* * Callers should tweak these to change the behavior of pp_* functions. diff --git a/log-tree.c b/log-tree.c index 78a5381d0e..48daf84d3b 100644 --- a/log-tree.c +++ b/log-tree.c @@ -603,7 +603,7 @@ void show_log(struct rev_info *opt) * Print header line of header.. */ - if (opt->commit_format == CMIT_FMT_EMAIL) { + if (cmit_fmt_is_mail(opt->commit_format)) { log_write_email_headers(opt, commit, &ctx.subject, &extra_headers, &ctx.need_8bit_cte); } else if (opt->commit_format != CMIT_FMT_USERFORMAT) { @@ -694,7 +694,7 @@ void show_log(struct rev_info *opt) if ((ctx.fmt != CMIT_FMT_USERFORMAT) && ctx.notes_message && *ctx.notes_message) { - if (ctx.fmt == CMIT_FMT_EMAIL) { + if (cmit_fmt_is_mail(ctx.fmt)) { strbuf_addstr(&msgbuf, "---\n"); opt->shown_dashes = 1; } diff --git a/pretty.c b/pretty.c index 87c44971a1..6abd8a1215 100644 --- a/pretty.c +++ b/pretty.c @@ -92,6 +92,7 @@ static void setup_commit_formats(void) { "medium", CMIT_FMT_MEDIUM, 0, 8 }, { "short", CMIT_FMT_SHORT, 0, 0 }, { "email", CMIT_FMT_EMAIL, 0, 0 }, + { "mboxrd", CMIT_FMT_MBOXRD, 0, 0 }, { "fuller", CMIT_FMT_FULLER, 0, 8 }, { "full", CMIT_FMT_FULL, 0, 8 }, { "oneline", CMIT_FMT_ONELINE, 1, 0 } @@ -444,7 +445,7 @@ void pp_user_info(struct pretty_print_context *pp, if (pp->mailmap) map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen); - if (pp->fmt == CMIT_FMT_EMAIL) { + if (cmit_fmt_is_mail(pp->fmt)) { if (pp->from_ident && ident_cmp(pp->from_ident, &ident)) { struct strbuf buf = STRBUF_INIT; @@ -494,6 +495,7 @@ void pp_user_info(struct pretty_print_context *pp, show_ident_date(&ident, &pp->date_mode)); break; case CMIT_FMT_EMAIL: + case CMIT_FMT_MBOXRD: strbuf_addf(sb, "Date: %s\n", show_ident_date(&ident, DATE_MODE(RFC2822))); break; @@ -535,7 +537,7 @@ static void add_merge_info(const struct pretty_print_context *pp, { struct commit_list *parent = commit->parents; - if ((pp->fmt == CMIT_FMT_ONELINE) || (pp->fmt == CMIT_FMT_EMAIL) || + if ((pp->fmt == CMIT_FMT_ONELINE) || (cmit_fmt_is_mail(pp->fmt)) || !parent || !parent->next) return; @@ -1614,7 +1616,7 @@ void pp_title_line(struct pretty_print_context *pp, if (pp->after_subject) { strbuf_addstr(sb, pp->after_subject); } - if (pp->fmt == CMIT_FMT_EMAIL) { + if (cmit_fmt_is_mail(pp->fmt)) { strbuf_addch(sb, '\n'); } @@ -1697,6 +1699,16 @@ static void pp_handle_indent(struct pretty_print_context *pp, strbuf_add(sb, line, linelen); } +static int is_mboxrd_from(const char *line, int len) +{ + /* + * a line matching /^From $/ here would only have len == 4 + * at this point because is_empty_line would've trimmed all + * trailing space + */ + return len > 4 && starts_with(line + strspn(line, ">"), "From "); +} + void pp_remainder(struct pretty_print_context *pp, const char **msg_p, struct strbuf *sb, @@ -1725,8 +1737,13 @@ void pp_remainder(struct pretty_print_context *pp, else if (pp->expand_tabs_in_log) strbuf_add_tabexpand(sb, pp->expand_tabs_in_log, line, linelen); - else + else { + if (pp->fmt == CMIT_FMT_MBOXRD && + is_mboxrd_from(line, linelen)) + strbuf_addch(sb, '>'); + strbuf_add(sb, line, linelen); + } strbuf_addch(sb, '\n'); } } @@ -1750,14 +1767,14 @@ void pretty_print_commit(struct pretty_print_context *pp, encoding = get_log_output_encoding(); msg = reencoded = logmsg_reencode(commit, NULL, encoding); - if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL) + if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt)) indent = 0; /* * We need to check and emit Content-type: to mark it * as 8-bit if we haven't done so. */ - if (pp->fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) { + if (cmit_fmt_is_mail(pp->fmt) && need_8bit_cte == 0) { int i, ch, in_body; for (in_body = i = 0; (ch = msg[i]); i++) { @@ -1785,7 +1802,7 @@ void pretty_print_commit(struct pretty_print_context *pp, msg = skip_empty_lines(msg); /* These formats treat the title line specially. */ - if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL) + if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt)) pp_title_line(pp, &msg, sb, encoding, need_8bit_cte); beginning_of_body = sb->len; @@ -1802,7 +1819,7 @@ void pretty_print_commit(struct pretty_print_context *pp, * format. Make sure we did not strip the blank line * between the header and the body. */ - if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) + if (cmit_fmt_is_mail(pp->fmt) && sb->len <= beginning_of_body) strbuf_addch(sb, '\n'); unuse_commit_buffer(commit, reencoded); diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 8049cad374..8a1cab5892 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -1565,4 +1565,45 @@ test_expect_success 'format-patch --base overrides format.useAutoBase' ' test_cmp expected actual ' +test_expect_success 'format-patch --pretty=mboxrd' ' + sp=" " && + cat >msg <<-INPUT_END && + mboxrd should escape the body + + From could trip up a loose mbox parser + >From extra escape for reversibility + >>From extra escape for reversibility 2 + from lower case not escaped + Fromm bad speling not escaped + From with leading space not escaped + + F + From + From$sp + From $sp + From $sp + INPUT_END + + cat >expect <<-INPUT_END && + >From could trip up a loose mbox parser + >>From extra escape for reversibility + >>>From extra escape for reversibility 2 + from lower case not escaped + Fromm bad speling not escaped + From with leading space not escaped + + F + From + From + From + From + INPUT_END + + C=$(git commit-tree HEAD^^{tree} -p HEAD patch && + git grep -h --no-index -A11 \ + "^>From could trip up a loose mbox parser" patch >actual && + test_cmp expect actual +' + test_done -- cgit v1.2.1 From c88098d7f19c6322fbd911bb89e2efd246bf75c4 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 5 Jun 2016 04:46:40 +0000 Subject: mailsplit: support unescaping mboxrd messages This will allow us to parse the output of --pretty=mboxrd and the output of other mboxrd generators. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- Documentation/git-mailsplit.txt | 7 ++++++- builtin/mailsplit.c | 18 ++++++++++++++++++ t/t5100-mailinfo.sh | 31 +++++++++++++++++++++++++++++++ t/t5100/0001mboxrd | 4 ++++ t/t5100/0002mboxrd | 5 +++++ t/t5100/sample.mboxrd | 19 +++++++++++++++++++ 6 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 t/t5100/0001mboxrd create mode 100644 t/t5100/0002mboxrd create mode 100644 t/t5100/sample.mboxrd diff --git a/Documentation/git-mailsplit.txt b/Documentation/git-mailsplit.txt index 4d1b871d96..e3b2a88c4b 100644 --- a/Documentation/git-mailsplit.txt +++ b/Documentation/git-mailsplit.txt @@ -8,7 +8,8 @@ git-mailsplit - Simple UNIX mbox splitter program SYNOPSIS -------- [verse] -'git mailsplit' [-b] [-f] [-d] [--keep-cr] -o [--] [(|)...] +'git mailsplit' [-b] [-f] [-d] [--keep-cr] [--mboxrd] + -o [--] [(|)...] DESCRIPTION ----------- @@ -47,6 +48,10 @@ OPTIONS --keep-cr:: Do not remove `\r` from lines ending with `\r\n`. +--mboxrd:: + Input is of the "mboxrd" format and "^>+From " line escaping is + reversed. + GIT --- Part of the linkgit:git[1] suite diff --git a/builtin/mailsplit.c b/builtin/mailsplit.c index 4859ede38a..30681681c1 100644 --- a/builtin/mailsplit.c +++ b/builtin/mailsplit.c @@ -45,6 +45,19 @@ static int is_from_line(const char *line, int len) static struct strbuf buf = STRBUF_INIT; static int keep_cr; +static int mboxrd; + +static int is_gtfrom(const struct strbuf *buf) +{ + size_t min = strlen(">From "); + size_t ngt; + + if (buf->len < min) + return 0; + + ngt = strspn(buf->buf, ">"); + return ngt && starts_with(buf->buf + ngt, "From "); +} /* Called with the first line (potentially partial) * already in buf[] -- normally that should begin with @@ -77,6 +90,9 @@ static int split_one(FILE *mbox, const char *name, int allow_bare) strbuf_addch(&buf, '\n'); } + if (mboxrd && is_gtfrom(&buf)) + strbuf_remove(&buf, 0, 1); + if (fwrite(buf.buf, 1, buf.len, output) != buf.len) die_errno("cannot write output"); @@ -271,6 +287,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix) keep_cr = 1; } else if ( arg[1] == 'o' && arg[2] ) { dir = arg+2; + } else if (!strcmp(arg, "--mboxrd")) { + mboxrd = 1; } else if ( arg[1] == '-' && !arg[2] ) { argp++; /* -- marks end of options */ break; diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh index 85b3df5e33..1a5a546230 100755 --- a/t/t5100-mailinfo.sh +++ b/t/t5100-mailinfo.sh @@ -111,4 +111,35 @@ test_expect_success 'mailinfo on message with quoted >From' ' test_cmp "$TEST_DIRECTORY"/t5100/quoted-from.expect quoted-from/msg ' +test_expect_success 'mailinfo unescapes with --mboxrd' ' + mkdir mboxrd && + git mailsplit -omboxrd --mboxrd \ + "$TEST_DIRECTORY"/t5100/sample.mboxrd >last && + test x"$(cat last)" = x2 && + for i in 0001 0002 + do + git mailinfo mboxrd/msg mboxrd/patch \ + mboxrd/out && + test_cmp "$TEST_DIRECTORY"/t5100/${i}mboxrd mboxrd/msg + done && + sp=" " && + echo "From " >expect && + echo "From " >>expect && + echo >> expect && + cat >sp <<-INPUT_END && + From mboxrd Mon Sep 17 00:00:00 2001 + From: trailing spacer + Subject: [PATCH] a commit with trailing space + + From$sp + >From$sp + + INPUT_END + + git mailsplit -f2 -omboxrd --mboxrd last && + test x"$(cat last)" = x1 && + git mailinfo mboxrd/msg mboxrd/patch From escaped +From not mangled but this line should have been escaped + diff --git a/t/t5100/0002mboxrd b/t/t5100/0002mboxrd new file mode 100644 index 0000000000..71343d41f2 --- /dev/null +++ b/t/t5100/0002mboxrd @@ -0,0 +1,5 @@ + >From unchanged + From also unchanged +no trailing space, no escaping necessary and '>' was intended: +>From + diff --git a/t/t5100/sample.mboxrd b/t/t5100/sample.mboxrd new file mode 100644 index 0000000000..79ad5ae0e7 --- /dev/null +++ b/t/t5100/sample.mboxrd @@ -0,0 +1,19 @@ +From mboxrd Mon Sep 17 00:00:00 2001 +From: mboxrd writer +Date: Fri, 9 Jun 2006 00:44:16 -0700 +Subject: [PATCH] a commit with escaped From lines + +>From the beginning, mbox should have been mboxrd +>>From escaped +From not mangled but this line should have been escaped + +From mboxrd Mon Sep 17 00:00:00 2001 +From: mboxrd writer +Date: Fri, 9 Jun 2006 00:44:16 -0700 +Subject: [PATCH 2/2] another with fake From lines + + >From unchanged + From also unchanged +no trailing space, no escaping necessary and '>' was intended: +>From + -- cgit v1.2.1 From d9925d1a714a440f4063f64e1bd776194d2dd918 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 5 Jun 2016 04:46:41 +0000 Subject: am: support --patch-format=mboxrd Combined with "git format-patch --pretty=mboxrd", this should allow us to round-trip commit messages with embedded mbox "From " lines without corruption. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- Documentation/git-am.txt | 3 ++- builtin/am.c | 14 +++++++++++--- t/t4150-am.sh | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt index 13cdd7f3b6..6348c29fea 100644 --- a/Documentation/git-am.txt +++ b/Documentation/git-am.txt @@ -116,7 +116,8 @@ default. You can use `--no-utf8` to override this. By default the command will try to detect the patch format automatically. This option allows the user to bypass the automatic detection and specify the patch format that the patch(es) should be - interpreted as. Valid formats are mbox, stgit, stgit-series and hg. + interpreted as. Valid formats are mbox, mboxrd, + stgit, stgit-series and hg. -i:: --interactive:: diff --git a/builtin/am.c b/builtin/am.c index 3dfe70b7a0..d5da5fe090 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -70,7 +70,8 @@ enum patch_format { PATCH_FORMAT_MBOX, PATCH_FORMAT_STGIT, PATCH_FORMAT_STGIT_SERIES, - PATCH_FORMAT_HG + PATCH_FORMAT_HG, + PATCH_FORMAT_MBOXRD }; enum keep_type { @@ -712,7 +713,8 @@ done: * Splits out individual email patches from `paths`, where each path is either * a mbox file or a Maildir. Returns 0 on success, -1 on failure. */ -static int split_mail_mbox(struct am_state *state, const char **paths, int keep_cr) +static int split_mail_mbox(struct am_state *state, const char **paths, + int keep_cr, int mboxrd) { struct child_process cp = CHILD_PROCESS_INIT; struct strbuf last = STRBUF_INIT; @@ -724,6 +726,8 @@ static int split_mail_mbox(struct am_state *state, const char **paths, int keep_ argv_array_push(&cp.args, "-b"); if (keep_cr) argv_array_push(&cp.args, "--keep-cr"); + if (mboxrd) + argv_array_push(&cp.args, "--mboxrd"); argv_array_push(&cp.args, "--"); argv_array_pushv(&cp.args, paths); @@ -965,13 +969,15 @@ static int split_mail(struct am_state *state, enum patch_format patch_format, switch (patch_format) { case PATCH_FORMAT_MBOX: - return split_mail_mbox(state, paths, keep_cr); + return split_mail_mbox(state, paths, keep_cr, 0); case PATCH_FORMAT_STGIT: return split_mail_conv(stgit_patch_to_mail, state, paths, keep_cr); case PATCH_FORMAT_STGIT_SERIES: return split_mail_stgit_series(state, paths, keep_cr); case PATCH_FORMAT_HG: return split_mail_conv(hg_patch_to_mail, state, paths, keep_cr); + case PATCH_FORMAT_MBOXRD: + return split_mail_mbox(state, paths, keep_cr, 1); default: die("BUG: invalid patch_format"); } @@ -2201,6 +2207,8 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int *opt_value = PATCH_FORMAT_STGIT_SERIES; else if (!strcmp(arg, "hg")) *opt_value = PATCH_FORMAT_HG; + else if (!strcmp(arg, "mboxrd")) + *opt_value = PATCH_FORMAT_MBOXRD; else return error(_("Invalid value for --patch-format: %s"), arg); return 0; diff --git a/t/t4150-am.sh b/t/t4150-am.sh index b41bd17264..9ce9424d15 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -957,4 +957,24 @@ test_expect_success 'am -s unexpected trailer block' ' test_cmp expect actual ' +test_expect_success 'am --patch-format=mboxrd handles mboxrd' ' + rm -fr .git/rebase-apply && + git checkout -f first && + echo mboxrd >>file && + git add file && + cat >msg <<-\INPUT_END && + mboxrd should escape the body + + From could trip up a loose mbox parser + >From extra escape for reversibility + INPUT_END + git commit -F msg && + git format-patch --pretty=mboxrd --stdout -1 >mboxrd1 && + grep "^>From could trip up a loose mbox parser" mboxrd1 && + git checkout -f first && + git am --patch-format=mboxrd mboxrd1 && + git cat-file commit HEAD | tail -n4 >out && + test_cmp msg out +' + test_done -- cgit v1.2.1 From a7d4c49a8201f75699bf59852ef8e345f41a12b6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 6 Jun 2016 13:11:02 -0700 Subject: builtin/apply: remove misleading comment on lock_file field Just like pointer field like prefix, the piece of memory pointed at by lock_file field is not owned by the apply_state structure. It is true that the caller needs to be careful about the lifetime rule for lockfile instances, but that is none of this API's business. Signed-off-by: Junio C Hamano --- builtin/apply.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 7338ee6b68..b3eb704725 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -52,10 +52,7 @@ struct apply_state { const char *prefix; int prefix_length; - /* - * Since lockfile.c keeps a linked list of all created - * lock_file structures, it isn't safe to free(lock_file). - */ + /* These are lock_file related */ struct lock_file *lock_file; int newfd; -- cgit v1.2.1 From 21d2a9e3cce6167a3cf69303ad5b35ec819a763b Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Fri, 10 Jun 2016 12:12:04 +0200 Subject: completion: factor out untracked file modes into a variable Signed-off-by: Thomas Braun Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e3918c87e3..a44e00b45e 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1098,6 +1098,8 @@ _git_clone () esac } +__git_untracked_file_modes="all no normal" + _git_commit () { case "$prev" in @@ -1119,7 +1121,7 @@ _git_commit () return ;; --untracked-files=*) - __gitcomp "all no normal" "" "${cur##--untracked-files=}" + __gitcomp "$__git_untracked_file_modes" "" "${cur##--untracked-files=}" return ;; --*) -- cgit v1.2.1 From 7c599e92aaba4a2ef07784858def25ced8512276 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Fri, 10 Jun 2016 12:12:05 +0200 Subject: completion: add __git_get_option_value helper This function allows to search the commmand line and config files for an option, long and short, with mandatory value. The function would return e.g. for the command line "git status -uno --untracked-files=all" the result "all" regardless of the config option. Signed-off-by: Thomas Braun Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a44e00b45e..14a8d0fe6e 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -803,6 +803,50 @@ __git_find_on_cmdline () done } +# Echo the value of an option set on the command line or config +# +# $1: short option name +# $2: long option name including = +# $3: list of possible values +# $4: config string (optional) +# +# example: +# result="$(__git_get_option_value "-d" "--do-something=" \ +# "yes no" "core.doSomething")" +# +# result is then either empty (no option set) or "yes" or "no" +# +# __git_get_option_value requires 3 arguments +__git_get_option_value () +{ + local c short_opt long_opt val + local result= values config_key word + + short_opt="$1" + long_opt="$2" + values="$3" + config_key="$4" + + ((c = $cword - 1)) + while [ $c -ge 0 ]; do + word="${words[c]}" + for val in $values; do + if [ "$short_opt$val" = "$word" ] || + [ "$long_opt$val" = "$word" ]; then + result="$val" + break 2 + fi + done + ((c--)) + done + + if [ -n "$config_key" ] && [ -z "$result" ]; then + result="$(git --git-dir="$(__gitdir)" config "$config_key")" + fi + + echo "$result" +} + __git_has_doubledash () { local c=1 -- cgit v1.2.1 From 634d2344e608c218e8163fe0b14e50ec1a62066a Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Fri, 10 Jun 2016 12:12:06 +0200 Subject: completion: add git status Signed-off-by: Thomas Braun Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 14a8d0fe6e..bb3acac1ec 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1825,6 +1825,56 @@ _git_stage () _git_add } +_git_status () +{ + local complete_opt + local untracked_state + + case "$cur" in + --ignore-submodules=*) + __gitcomp "none untracked dirty all" "" "${cur##--ignore-submodules=}" + return + ;; + --untracked-files=*) + __gitcomp "$__git_untracked_file_modes" "" "${cur##--untracked-files=}" + return + ;; + --column=*) + __gitcomp " + always never auto column row plain dense nodense + " "" "${cur##--column=}" + return + ;; + --*) + __gitcomp " + --short --branch --porcelain --long --verbose + --untracked-files= --ignore-submodules= --ignored + --column= --no-column + " + return + ;; + esac + + untracked_state="$(__git_get_option_value "-u" "--untracked-files=" \ + "$__git_untracked_file_modes" "status.showUntrackedFiles")" + + case "$untracked_state" in + no) + # --ignored option does not matter + complete_opt= + ;; + all|normal|*) + complete_opt="--cached --directory --no-empty-directory --others" + + if [ -n "$(__git_find_on_cmdline "--ignored")" ]; then + complete_opt="$complete_opt --ignored --exclude=*" + fi + ;; + esac + + __git_complete_index_file "$complete_opt" +} + __git_config_get_set_variables () { local prevword word config_file= c=$cword -- cgit v1.2.1 From efe472813d60befd72d6e2797934c90b22a26c93 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Fri, 22 Apr 2016 00:02:50 +0200 Subject: ref_transaction_commit(): remove local variables n and updates These microoptimizations don't make a significant difference in speed. And they cause problems if somebody ever wants to modify the function to add updates to a transaction as part of processing it, as will happen shortly. Make the same changes in initial_ref_transaction_commit(). Signed-off-by: Michael Haggerty --- refs/files-backend.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 80d346fd38..814e230fa3 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -3076,8 +3076,6 @@ int ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { int ret = 0, i; - int n = transaction->nr; - struct ref_update **updates = transaction->updates; struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; struct string_list_item *ref_to_delete; struct string_list affected_refnames = STRING_LIST_INIT_NODUP; @@ -3087,14 +3085,15 @@ int ref_transaction_commit(struct ref_transaction *transaction, if (transaction->state != REF_TRANSACTION_OPEN) die("BUG: commit called for transaction that is not open"); - if (!n) { + if (!transaction->nr) { transaction->state = REF_TRANSACTION_CLOSED; return 0; } /* Fail if a refname appears more than once in the transaction: */ - for (i = 0; i < n; i++) - string_list_append(&affected_refnames, updates[i]->refname); + for (i = 0; i < transaction->nr; i++) + string_list_append(&affected_refnames, + transaction->updates[i]->refname); string_list_sort(&affected_refnames); if (ref_update_reject_duplicates(&affected_refnames, err)) { ret = TRANSACTION_GENERIC_ERROR; @@ -3107,8 +3106,8 @@ int ref_transaction_commit(struct ref_transaction *transaction, * lockfiles, ready to be activated. Only keep one lockfile * open at a time to avoid running out of file descriptors. */ - for (i = 0; i < n; i++) { - struct ref_update *update = updates[i]; + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; if ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1)) @@ -3178,8 +3177,8 @@ int ref_transaction_commit(struct ref_transaction *transaction, } /* Perform updates first so live commits remain referenced */ - for (i = 0; i < n; i++) { - struct ref_update *update = updates[i]; + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; if (update->flags & REF_NEEDS_COMMIT) { if (commit_ref_update(update->lock, @@ -3197,8 +3196,8 @@ int ref_transaction_commit(struct ref_transaction *transaction, } /* Perform deletes now that updates are safely completed */ - for (i = 0; i < n; i++) { - struct ref_update *update = updates[i]; + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; if (update->flags & REF_DELETING) { if (delete_ref_loose(update->lock, update->type, err)) { @@ -3223,9 +3222,9 @@ int ref_transaction_commit(struct ref_transaction *transaction, cleanup: transaction->state = REF_TRANSACTION_CLOSED; - for (i = 0; i < n; i++) - if (updates[i]->lock) - unlock_ref(updates[i]->lock); + for (i = 0; i < transaction->nr; i++) + if (transaction->updates[i]->lock) + unlock_ref(transaction->updates[i]->lock); string_list_clear(&refs_to_delete, 0); string_list_clear(&affected_refnames, 0); return ret; @@ -3243,8 +3242,6 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { int ret = 0, i; - int n = transaction->nr; - struct ref_update **updates = transaction->updates; struct string_list affected_refnames = STRING_LIST_INIT_NODUP; assert(err); @@ -3253,8 +3250,9 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction, die("BUG: commit called for transaction that is not open"); /* Fail if a refname appears more than once in the transaction: */ - for (i = 0; i < n; i++) - string_list_append(&affected_refnames, updates[i]->refname); + for (i = 0; i < transaction->nr; i++) + string_list_append(&affected_refnames, + transaction->updates[i]->refname); string_list_sort(&affected_refnames); if (ref_update_reject_duplicates(&affected_refnames, err)) { ret = TRANSACTION_GENERIC_ERROR; @@ -3276,8 +3274,8 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction, if (for_each_rawref(ref_present, &affected_refnames)) die("BUG: initial ref transaction called with existing refs"); - for (i = 0; i < n; i++) { - struct ref_update *update = updates[i]; + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; if ((update->flags & REF_HAVE_OLD) && !is_null_sha1(update->old_sha1)) @@ -3297,8 +3295,8 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction, goto cleanup; } - for (i = 0; i < n; i++) { - struct ref_update *update = updates[i]; + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; if ((update->flags & REF_HAVE_NEW) && !is_null_sha1(update->new_sha1)) -- cgit v1.2.1 From 3a0b6b9aba844075e802a6dc4c24622b34ab535b Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Tue, 26 Apr 2016 03:06:23 +0200 Subject: read_raw_ref(): rename flags argument to type This will hopefully reduce confusion with the "flags" arguments that are used in many functions in this module as an input parameter to choose how the function should operate. Signed-off-by: Michael Haggerty --- refs/files-backend.c | 18 +++++++++--------- refs/refs-internal.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 814e230fa3..1657ce7512 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1395,18 +1395,18 @@ static int resolve_missing_loose_ref(const char *refname, * * If the ref is symbolic, fill in *symref with the referrent * (e.g. "refs/heads/master") and return 0. The caller is responsible - * for validating the referrent. Set REF_ISSYMREF in flags. + * for validating the referrent. Set REF_ISSYMREF in type. * * If the ref doesn't exist, set errno to ENOENT and return -1. * * If the ref exists but is neither a symbolic ref nor a sha1, it is - * broken. Set REF_ISBROKEN in flags, set errno to EINVAL, and return + * broken. Set REF_ISBROKEN in type, set errno to EINVAL, and return * -1. * * If there is another error reading the ref, set errno appropriately and * return -1. * - * Backend-specific flags might be set in flags as well, regardless of + * Backend-specific flags might be set in type as well, regardless of * outcome. * * sb_path is workspace: the caller should allocate and free it. @@ -1419,7 +1419,7 @@ static int resolve_missing_loose_ref(const char *refname, * refname will still be valid and unchanged. */ int read_raw_ref(const char *refname, unsigned char *sha1, - struct strbuf *symref, unsigned int *flags) + struct strbuf *symref, unsigned int *type) { struct strbuf sb_contents = STRBUF_INIT; struct strbuf sb_path = STRBUF_INIT; @@ -1448,7 +1448,7 @@ stat_ref: if (lstat(path, &st) < 0) { if (errno != ENOENT) goto out; - if (resolve_missing_loose_ref(refname, sha1, flags)) { + if (resolve_missing_loose_ref(refname, sha1, type)) { errno = ENOENT; goto out; } @@ -1469,7 +1469,7 @@ stat_ref: if (starts_with(sb_contents.buf, "refs/") && !check_refname_format(sb_contents.buf, 0)) { strbuf_swap(&sb_contents, symref); - *flags |= REF_ISSYMREF; + *type |= REF_ISSYMREF; ret = 0; goto out; } @@ -1482,7 +1482,7 @@ stat_ref: * ref is supposed to be, there could still be a * packed ref: */ - if (resolve_missing_loose_ref(refname, sha1, flags)) { + if (resolve_missing_loose_ref(refname, sha1, type)) { errno = EISDIR; goto out; } @@ -1519,7 +1519,7 @@ stat_ref: strbuf_reset(symref); strbuf_addstr(symref, buf); - *flags |= REF_ISSYMREF; + *type |= REF_ISSYMREF; ret = 0; goto out; } @@ -1530,7 +1530,7 @@ stat_ref: */ if (get_sha1_hex(buf, sha1) || (buf[40] != '\0' && !isspace(buf[40]))) { - *flags |= REF_ISBROKEN; + *type |= REF_ISBROKEN; errno = EINVAL; goto out; } diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 3a4f634cb4..0b047f67ba 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -210,6 +210,6 @@ int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn, int trim, int flags, void *cb_data); int read_raw_ref(const char *refname, unsigned char *sha1, - struct strbuf *symref, unsigned int *flags); + struct strbuf *symref, unsigned int *type); #endif /* REFS_REFS_INTERNAL_H */ -- cgit v1.2.1 From fa96ea1b883bf83fc488f999c58396bbab1860c1 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Fri, 22 Apr 2016 01:11:17 +0200 Subject: read_raw_ref(): clear *type at start of function This is more convenient and less error-prone for callers. Signed-off-by: Michael Haggerty --- refs/files-backend.c | 1 + 1 file changed, 1 insertion(+) diff --git a/refs/files-backend.c b/refs/files-backend.c index 1657ce7512..3c99eef42a 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1430,6 +1430,7 @@ int read_raw_ref(const char *refname, unsigned char *sha1, int ret = -1; int save_errno; + *type = 0; strbuf_reset(&sb_path); strbuf_git_path(&sb_path, "%s", refname); path = sb_path.buf; -- cgit v1.2.1 From 92b380931ee8beacb0c09635432b38a02b9fcc7e Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Fri, 22 Apr 2016 01:11:17 +0200 Subject: read_raw_ref(): rename symref argument to referent After all, it doesn't hold the symbolic reference, but rather the reference referred to. Signed-off-by: Michael Haggerty --- refs/files-backend.c | 21 +++++++++++---------- refs/refs-internal.h | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 3c99eef42a..a23ade4ffb 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1393,9 +1393,10 @@ static int resolve_missing_loose_ref(const char *refname, * * If the ref is a sha1, fill in sha1 and return 0. * - * If the ref is symbolic, fill in *symref with the referrent - * (e.g. "refs/heads/master") and return 0. The caller is responsible - * for validating the referrent. Set REF_ISSYMREF in type. + * If the ref is symbolic, fill in *referent with the name of the + * branch to which it refers (e.g. "refs/heads/master") and return 0. + * The caller is responsible for validating the referent. Set + * REF_ISSYMREF in type. * * If the ref doesn't exist, set errno to ENOENT and return -1. * @@ -1411,15 +1412,15 @@ static int resolve_missing_loose_ref(const char *refname, * * sb_path is workspace: the caller should allocate and free it. * - * It is OK for refname to point into symref. In this case: - * - if the function succeeds with REF_ISSYMREF, symref will be + * It is OK for refname to point into referent. In this case: + * - if the function succeeds with REF_ISSYMREF, referent will be * overwritten and the memory pointed to by refname might be changed * or even freed. - * - in all other cases, symref will be untouched, and therefore + * - in all other cases, referent will be untouched, and therefore * refname will still be valid and unchanged. */ int read_raw_ref(const char *refname, unsigned char *sha1, - struct strbuf *symref, unsigned int *type) + struct strbuf *referent, unsigned int *type) { struct strbuf sb_contents = STRBUF_INIT; struct strbuf sb_path = STRBUF_INIT; @@ -1469,7 +1470,7 @@ stat_ref: } if (starts_with(sb_contents.buf, "refs/") && !check_refname_format(sb_contents.buf, 0)) { - strbuf_swap(&sb_contents, symref); + strbuf_swap(&sb_contents, referent); *type |= REF_ISSYMREF; ret = 0; goto out; @@ -1518,8 +1519,8 @@ stat_ref: while (isspace(*buf)) buf++; - strbuf_reset(symref); - strbuf_addstr(symref, buf); + strbuf_reset(referent); + strbuf_addstr(referent, buf); *type |= REF_ISSYMREF; ret = 0; goto out; diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 0b047f67ba..37a1a37ea0 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -210,6 +210,6 @@ int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn, int trim, int flags, void *cb_data); int read_raw_ref(const char *refname, unsigned char *sha1, - struct strbuf *symref, unsigned int *type); + struct strbuf *referent, unsigned int *type); #endif /* REFS_REFS_INTERNAL_H */ -- cgit v1.2.1 From bb462b00286902f6cdbb66bb418c59b5c7894e0d Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Sun, 24 Apr 2016 08:10:30 +0200 Subject: read_raw_ref(): improve docstring Among other things, document the (important!) requirement that input refname be checked for safety before calling this function. Signed-off-by: Michael Haggerty --- refs/files-backend.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index a23ade4ffb..c41cf432d1 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1389,33 +1389,40 @@ static int resolve_missing_loose_ref(const char *refname, } /* - * Read a raw ref from the filesystem or packed refs file. + * Read the specified reference from the filesystem or packed refs + * file, non-recursively. Set type to describe the reference, and: * - * If the ref is a sha1, fill in sha1 and return 0. + * - If refname is the name of a normal reference, fill in sha1 + * (leaving referent unchanged). * - * If the ref is symbolic, fill in *referent with the name of the - * branch to which it refers (e.g. "refs/heads/master") and return 0. - * The caller is responsible for validating the referent. Set - * REF_ISSYMREF in type. + * - If refname is the name of a symbolic reference, write the full + * name of the reference to which it refers (e.g. + * "refs/heads/master") to referent and set the REF_ISSYMREF bit in + * type (leaving sha1 unchanged). The caller is responsible for + * validating that referent is a valid reference name. * - * If the ref doesn't exist, set errno to ENOENT and return -1. + * WARNING: refname might be used as part of a filename, so it is + * important from a security standpoint that it be safe in the sense + * of refname_is_safe(). Moreover, for symrefs this function sets + * referent to whatever the repository says, which might not be a + * properly-formatted or even safe reference name. NEITHER INPUT NOR + * OUTPUT REFERENCE NAMES ARE VALIDATED WITHIN THIS FUNCTION. * - * If the ref exists but is neither a symbolic ref nor a sha1, it is - * broken. Set REF_ISBROKEN in type, set errno to EINVAL, and return - * -1. - * - * If there is another error reading the ref, set errno appropriately and - * return -1. + * Return 0 on success. If the ref doesn't exist, set errno to ENOENT + * and return -1. If the ref exists but is neither a symbolic ref nor + * a sha1, it is broken; set REF_ISBROKEN in type, set errno to + * EINVAL, and return -1. If there is another error reading the ref, + * set errno appropriately and return -1. * * Backend-specific flags might be set in type as well, regardless of * outcome. * - * sb_path is workspace: the caller should allocate and free it. + * It is OK for refname to point into referent. If so: * - * It is OK for refname to point into referent. In this case: * - if the function succeeds with REF_ISSYMREF, referent will be - * overwritten and the memory pointed to by refname might be changed - * or even freed. + * overwritten and the memory formerly pointed to by it might be + * changed or even freed. + * * - in all other cases, referent will be untouched, and therefore * refname will still be valid and unchanged. */ -- cgit v1.2.1 From cf596442c6a18268f3f0d95cf7615a613102746f Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Fri, 6 May 2016 17:25:31 +0200 Subject: read_raw_ref(): move docstring to header file Signed-off-by: Michael Haggerty --- refs/files-backend.c | 38 -------------------------------------- refs/refs-internal.h | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index c41cf432d1..c2bd7b8317 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1388,44 +1388,6 @@ static int resolve_missing_loose_ref(const char *refname, return -1; } -/* - * Read the specified reference from the filesystem or packed refs - * file, non-recursively. Set type to describe the reference, and: - * - * - If refname is the name of a normal reference, fill in sha1 - * (leaving referent unchanged). - * - * - If refname is the name of a symbolic reference, write the full - * name of the reference to which it refers (e.g. - * "refs/heads/master") to referent and set the REF_ISSYMREF bit in - * type (leaving sha1 unchanged). The caller is responsible for - * validating that referent is a valid reference name. - * - * WARNING: refname might be used as part of a filename, so it is - * important from a security standpoint that it be safe in the sense - * of refname_is_safe(). Moreover, for symrefs this function sets - * referent to whatever the repository says, which might not be a - * properly-formatted or even safe reference name. NEITHER INPUT NOR - * OUTPUT REFERENCE NAMES ARE VALIDATED WITHIN THIS FUNCTION. - * - * Return 0 on success. If the ref doesn't exist, set errno to ENOENT - * and return -1. If the ref exists but is neither a symbolic ref nor - * a sha1, it is broken; set REF_ISBROKEN in type, set errno to - * EINVAL, and return -1. If there is another error reading the ref, - * set errno appropriately and return -1. - * - * Backend-specific flags might be set in type as well, regardless of - * outcome. - * - * It is OK for refname to point into referent. If so: - * - * - if the function succeeds with REF_ISSYMREF, referent will be - * overwritten and the memory formerly pointed to by it might be - * changed or even freed. - * - * - in all other cases, referent will be untouched, and therefore - * refname will still be valid and unchanged. - */ int read_raw_ref(const char *refname, unsigned char *sha1, struct strbuf *referent, unsigned int *type) { diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 37a1a37ea0..de7722e323 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -209,6 +209,44 @@ int rename_ref_available(const char *oldname, const char *newname); int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn, int trim, int flags, void *cb_data); +/* + * Read the specified reference from the filesystem or packed refs + * file, non-recursively. Set type to describe the reference, and: + * + * - If refname is the name of a normal reference, fill in sha1 + * (leaving referent unchanged). + * + * - If refname is the name of a symbolic reference, write the full + * name of the reference to which it refers (e.g. + * "refs/heads/master") to referent and set the REF_ISSYMREF bit in + * type (leaving sha1 unchanged). The caller is responsible for + * validating that referent is a valid reference name. + * + * WARNING: refname might be used as part of a filename, so it is + * important from a security standpoint that it be safe in the sense + * of refname_is_safe(). Moreover, for symrefs this function sets + * referent to whatever the repository says, which might not be a + * properly-formatted or even safe reference name. NEITHER INPUT NOR + * OUTPUT REFERENCE NAMES ARE VALIDATED WITHIN THIS FUNCTION. + * + * Return 0 on success. If the ref doesn't exist, set errno to ENOENT + * and return -1. If the ref exists but is neither a symbolic ref nor + * a sha1, it is broken; set REF_ISBROKEN in type, set errno to + * EINVAL, and return -1. If there is another error reading the ref, + * set errno appropriately and return -1. + * + * Backend-specific flags might be set in type as well, regardless of + * outcome. + * + * It is OK for refname to point into referent. If so: + * + * - if the function succeeds with REF_ISSYMREF, referent will be + * overwritten and the memory formerly pointed to by it might be + * changed or even freed. + * + * - in all other cases, referent will be untouched, and therefore + * refname will still be valid and unchanged. + */ int read_raw_ref(const char *refname, unsigned char *sha1, struct strbuf *referent, unsigned int *type); -- cgit v1.2.1 From bcb497d0f83f9c3e60f00fd2cc87130923329ed9 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Fri, 22 Apr 2016 09:13:00 +0200 Subject: lock_ref_sha1_basic(): remove unneeded local variable resolve_ref_unsafe() can cope with being called with NULL passed to its flags argument. So lock_ref_sha1_basic() can just hand its own type parameter through. Signed-off-by: Michael Haggerty --- refs/files-backend.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index c2bd7b8317..dc247e0e7b 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1738,7 +1738,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, const unsigned char *old_sha1, const struct string_list *extras, const struct string_list *skip, - unsigned int flags, int *type_p, + unsigned int flags, int *type, struct strbuf *err) { struct strbuf ref_file = STRBUF_INIT; @@ -1746,7 +1746,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, const char *orig_refname = refname; struct ref_lock *lock; int last_errno = 0; - int type; int lflags = 0; int mustexist = (old_sha1 && !is_null_sha1(old_sha1)); int resolve_flags = 0; @@ -1766,7 +1765,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, } refname = resolve_ref_unsafe(refname, resolve_flags, - lock->old_oid.hash, &type); + lock->old_oid.hash, type); if (!refname && errno == EISDIR) { /* * we are trying to lock foo but we used to @@ -1784,10 +1783,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, goto error_return; } refname = resolve_ref_unsafe(orig_refname, resolve_flags, - lock->old_oid.hash, &type); + lock->old_oid.hash, type); } - if (type_p) - *type_p = type; if (!refname) { last_errno = errno; if (last_errno != ENOTDIR || -- cgit v1.2.1 From 0568c8e9dce2aa0dd18f41f23e3465f3639e371e Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 27 Apr 2016 15:21:36 +0200 Subject: refs: make error messages more consistent * Always start error messages with a lower-case letter. * Always enclose reference names in single quotes. Signed-off-by: Michael Haggerty --- refs.c | 8 ++++---- refs/files-backend.c | 32 ++++++++++++++++---------------- t/t1400-update-ref.sh | 4 ++-- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/refs.c b/refs.c index b18d9959af..ba14105959 100644 --- a/refs.c +++ b/refs.c @@ -504,7 +504,7 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1, filename = git_path("%s", pseudoref); fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR); if (fd < 0) { - strbuf_addf(err, "Could not open '%s' for writing: %s", + strbuf_addf(err, "could not open '%s' for writing: %s", filename, strerror(errno)); return -1; } @@ -515,14 +515,14 @@ static int write_pseudoref(const char *pseudoref, const unsigned char *sha1, if (read_ref(pseudoref, actual_old_sha1)) die("could not read ref '%s'", pseudoref); if (hashcmp(actual_old_sha1, old_sha1)) { - strbuf_addf(err, "Unexpected sha1 when writing %s", pseudoref); + strbuf_addf(err, "unexpected sha1 when writing '%s'", pseudoref); rollback_lock_file(&lock); goto done; } } if (write_in_full(fd, buf.buf, buf.len) != buf.len) { - strbuf_addf(err, "Could not write to '%s'", filename); + strbuf_addf(err, "could not write to '%s'", filename); rollback_lock_file(&lock); goto done; } @@ -792,7 +792,7 @@ int ref_transaction_update(struct ref_transaction *transaction, if (new_sha1 && !is_null_sha1(new_sha1) && check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { - strbuf_addf(err, "refusing to update ref with bad name %s", + strbuf_addf(err, "refusing to update ref with bad name '%s'", refname); return -1; } diff --git a/refs/files-backend.c b/refs/files-backend.c index dc247e0e7b..c978fe49c9 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1701,7 +1701,7 @@ static int verify_lock(struct ref_lock *lock, lock->old_oid.hash, NULL)) { if (old_sha1) { int save_errno = errno; - strbuf_addf(err, "can't verify ref %s", lock->ref_name); + strbuf_addf(err, "can't verify ref '%s'", lock->ref_name); errno = save_errno; return -1; } else { @@ -1710,7 +1710,7 @@ static int verify_lock(struct ref_lock *lock, } } if (old_sha1 && hashcmp(lock->old_oid.hash, old_sha1)) { - strbuf_addf(err, "ref %s is at %s but expected %s", + strbuf_addf(err, "ref '%s' is at %s but expected %s", lock->ref_name, sha1_to_hex(lock->old_oid.hash), sha1_to_hex(old_sha1)); @@ -1790,7 +1790,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, if (last_errno != ENOTDIR || !verify_refname_available_dir(orig_refname, extras, skip, get_loose_refs(&ref_cache), err)) - strbuf_addf(err, "unable to resolve reference %s: %s", + strbuf_addf(err, "unable to resolve reference '%s': %s", orig_refname, strerror(last_errno)); goto error_return; @@ -1828,7 +1828,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, /* fall through */ default: last_errno = errno; - strbuf_addf(err, "unable to create directory for %s", + strbuf_addf(err, "unable to create directory for '%s'", ref_file.buf); goto error_return; } @@ -2473,7 +2473,7 @@ static int log_ref_setup(const char *refname, struct strbuf *logfile, struct str strbuf_git_path(logfile, "logs/%s", refname); if (force_create || should_autocreate_reflog(refname)) { if (safe_create_leading_directories(logfile->buf) < 0) { - strbuf_addf(err, "unable to create directory for %s: " + strbuf_addf(err, "unable to create directory for '%s': " "%s", logfile->buf, strerror(errno)); return -1; } @@ -2487,7 +2487,7 @@ static int log_ref_setup(const char *refname, struct strbuf *logfile, struct str if (errno == EISDIR) { if (remove_empty_directories(logfile)) { - strbuf_addf(err, "There are still logs under " + strbuf_addf(err, "there are still logs under " "'%s'", logfile->buf); return -1; } @@ -2495,7 +2495,7 @@ static int log_ref_setup(const char *refname, struct strbuf *logfile, struct str } if (logfd < 0) { - strbuf_addf(err, "unable to append to %s: %s", + strbuf_addf(err, "unable to append to '%s': %s", logfile->buf, strerror(errno)); return -1; } @@ -2564,13 +2564,13 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1, result = log_ref_write_fd(logfd, old_sha1, new_sha1, git_committer_info(0), msg); if (result) { - strbuf_addf(err, "unable to append to %s: %s", logfile->buf, + strbuf_addf(err, "unable to append to '%s': %s", logfile->buf, strerror(errno)); close(logfd); return -1; } if (close(logfd)) { - strbuf_addf(err, "unable to append to %s: %s", logfile->buf, + strbuf_addf(err, "unable to append to '%s': %s", logfile->buf, strerror(errno)); return -1; } @@ -2611,14 +2611,14 @@ static int write_ref_to_lockfile(struct ref_lock *lock, o = parse_object(sha1); if (!o) { strbuf_addf(err, - "Trying to write ref %s with nonexistent object %s", + "trying to write ref '%s' with nonexistent object %s", lock->ref_name, sha1_to_hex(sha1)); unlock_ref(lock); return -1; } if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) { strbuf_addf(err, - "Trying to write non-commit object %s to branch %s", + "trying to write non-commit object %s to branch '%s'", sha1_to_hex(sha1), lock->ref_name); unlock_ref(lock); return -1; @@ -2628,7 +2628,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock, write_in_full(fd, &term, 1) != 1 || close_ref(lock) < 0) { strbuf_addf(err, - "Couldn't write %s", get_lock_file_path(lock->lk)); + "couldn't write '%s'", get_lock_file_path(lock->lk)); unlock_ref(lock); return -1; } @@ -2649,7 +2649,7 @@ static int commit_ref_update(struct ref_lock *lock, (strcmp(lock->ref_name, lock->orig_ref_name) && log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0)) { char *old_msg = strbuf_detach(err, NULL); - strbuf_addf(err, "Cannot update the ref '%s': %s", + strbuf_addf(err, "cannot update the ref '%s': %s", lock->ref_name, old_msg); free(old_msg); unlock_ref(lock); @@ -2684,7 +2684,7 @@ static int commit_ref_update(struct ref_lock *lock, } } if (commit_ref(lock)) { - strbuf_addf(err, "Couldn't set %s", lock->ref_name); + strbuf_addf(err, "couldn't set '%s'", lock->ref_name); unlock_ref(lock); return -1; } @@ -3033,7 +3033,7 @@ static int ref_update_reject_duplicates(struct string_list *refnames, for (i = 1; i < n; i++) if (!strcmp(refnames->items[i - 1].string, refnames->items[i].string)) { strbuf_addf(err, - "Multiple updates for ref '%s' not allowed.", + "multiple updates for ref '%s' not allowed.", refnames->items[i].string); return 1; } @@ -3137,7 +3137,7 @@ int ref_transaction_commit(struct ref_transaction *transaction, * Close it to free up the file descriptor: */ if (close_ref(update->lock)) { - strbuf_addf(err, "Couldn't close %s.lock", + strbuf_addf(err, "couldn't close '%s.lock'", update->refname); goto cleanup; } diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index af1b20dd5c..40b0ccedfc 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -479,7 +479,7 @@ test_expect_success 'stdin fails with duplicate refs' ' create $a $m EOF test_must_fail git update-ref --stdin err && - grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err + grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed." err ' test_expect_success 'stdin create ref works' ' @@ -880,7 +880,7 @@ test_expect_success 'stdin -z fails option with unknown name' ' test_expect_success 'stdin -z fails with duplicate refs' ' printf $F "create $a" "$m" "create $b" "$m" "create $a" "$m" >stdin && test_must_fail git update-ref -z --stdin err && - grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err + grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed." err ' test_expect_success 'stdin -z create ref works' ' -- cgit v1.2.1 From c52ce248d63a185eb0a616b361d1fd72c5c66451 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Sun, 24 Apr 2016 09:48:26 +0200 Subject: ref_transaction_create(): disallow recursive pruning It is nonsensical (and a little bit dangerous) to use REF_ISPRUNING without REF_NODEREF. Forbid it explicitly. Change the one REF_ISPRUNING caller to pass REF_NODEREF too. Signed-off-by: Michael Haggerty --- refs.c | 3 +++ refs/files-backend.c | 2 +- refs/refs-internal.h | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/refs.c b/refs.c index ba14105959..5dc2473fbb 100644 --- a/refs.c +++ b/refs.c @@ -790,6 +790,9 @@ int ref_transaction_update(struct ref_transaction *transaction, if (transaction->state != REF_TRANSACTION_OPEN) die("BUG: update called for transaction that is not open"); + if ((flags & REF_ISPRUNING) && !(flags & REF_NODEREF)) + die("BUG: REF_ISPRUNING set without REF_NODEREF"); + if (new_sha1 && !is_null_sha1(new_sha1) && check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { strbuf_addf(err, "refusing to update ref with bad name '%s'", diff --git a/refs/files-backend.c b/refs/files-backend.c index c978fe49c9..35d37ce58b 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2087,7 +2087,7 @@ static void prune_ref(struct ref_to_prune *r) transaction = ref_transaction_begin(&err); if (!transaction || ref_transaction_delete(transaction, r->name, r->sha1, - REF_ISPRUNING, NULL, &err) || + REF_ISPRUNING | REF_NODEREF, NULL, &err) || ref_transaction_commit(transaction, &err)) { ref_transaction_free(transaction); error("%s", err.buf); diff --git a/refs/refs-internal.h b/refs/refs-internal.h index de7722e323..1f94f7a262 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -15,7 +15,7 @@ /* * Used as a flag in ref_update::flags when a loose ref is being - * pruned. + * pruned. This flag must only be used when REF_NODEREF is set. */ #define REF_ISPRUNING 0x04 -- cgit v1.2.1 From 5a563d4ad17a66aabeacfd0f221ac45c07bc4ee8 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 25 Apr 2016 11:58:23 +0200 Subject: ref_transaction_commit(): correctly report close_ref() failure Signed-off-by: Michael Haggerty --- refs/files-backend.c | 1 + 1 file changed, 1 insertion(+) diff --git a/refs/files-backend.c b/refs/files-backend.c index 35d37ce58b..85e1e1c759 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -3139,6 +3139,7 @@ int ref_transaction_commit(struct ref_transaction *transaction, if (close_ref(update->lock)) { strbuf_addf(err, "couldn't close '%s.lock'", update->refname); + ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } } -- cgit v1.2.1 From 8bb0455367a17bd7428e02f835e3f55c8cd168da Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 25 Apr 2016 10:42:19 +0200 Subject: delete_branches(): use resolve_refdup() The return value of resolve_ref_unsafe() is not guaranteed to stay around as long as we need it, so use resolve_refdup() instead. Signed-off-by: Michael Haggerty --- builtin/branch.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/builtin/branch.c b/builtin/branch.c index 0adba629d2..ae5568845c 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -212,7 +212,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, die(_("Couldn't look up commit object for HEAD")); } for (i = 0; i < argc; i++, strbuf_release(&bname)) { - const char *target; + char *target = NULL; int flags = 0; strbuf_branchname(&bname, argv[i]); @@ -231,11 +231,11 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, } } - target = resolve_ref_unsafe(name, - RESOLVE_REF_READING - | RESOLVE_REF_NO_RECURSE - | RESOLVE_REF_ALLOW_BAD_NAME, - sha1, &flags); + target = resolve_refdup(name, + RESOLVE_REF_READING + | RESOLVE_REF_NO_RECURSE + | RESOLVE_REF_ALLOW_BAD_NAME, + sha1, &flags); if (!target) { error(remote_branch ? _("remote-tracking branch '%s' not found.") @@ -248,7 +248,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, check_branch_commit(bname.buf, name, sha1, head_rev, kinds, force)) { ret = 1; - continue; + goto next; } if (delete_ref(name, is_null_sha1(sha1) ? NULL : sha1, @@ -258,7 +258,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, : _("Error deleting branch '%s'"), bname.buf); ret = 1; - continue; + goto next; } if (!quiet) { printf(remote_branch @@ -270,6 +270,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, : find_unique_abbrev(sha1, DEFAULT_ABBREV)); } delete_branch_config(bname.buf); + + next: + free(target); } free(name); -- cgit v1.2.1 From d99aa884dff33d48d5aab8c9cf989a25c779fd70 Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 24 Feb 2016 17:58:50 -0500 Subject: refs: allow log-only updates The refs infrastructure learns about log-only ref updates, which only update the reflog. Later, we will use this to separate symbolic reference resolution from ref updating. Signed-off-by: David Turner Signed-off-by: Junio C Hamano Signed-off-by: Michael Haggerty --- refs/files-backend.c | 16 ++++++++++------ refs/refs-internal.h | 7 +++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 85e1e1c759..2f98eebe94 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2683,7 +2683,7 @@ static int commit_ref_update(struct ref_lock *lock, } } } - if (commit_ref(lock)) { + if (!(flags & REF_LOG_ONLY) && commit_ref(lock)) { strbuf_addf(err, "couldn't set '%s'", lock->ref_name); unlock_ref(lock); return -1; @@ -3101,7 +3101,8 @@ int ref_transaction_commit(struct ref_transaction *transaction, goto cleanup; } if ((update->flags & REF_HAVE_NEW) && - !(update->flags & REF_DELETING)) { + !(update->flags & REF_DELETING) && + !(update->flags & REF_LOG_ONLY)) { int overwriting_symref = ((update->type & REF_ISSYMREF) && (update->flags & REF_NODEREF)); @@ -3133,8 +3134,9 @@ int ref_transaction_commit(struct ref_transaction *transaction, } if (!(update->flags & REF_NEEDS_COMMIT)) { /* - * We didn't have to write anything to the lockfile. - * Close it to free up the file descriptor: + * We didn't call write_ref_to_lockfile(), so + * the lockfile is still open. Close it to + * free up the file descriptor: */ if (close_ref(update->lock)) { strbuf_addf(err, "couldn't close '%s.lock'", @@ -3149,7 +3151,8 @@ int ref_transaction_commit(struct ref_transaction *transaction, for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; - if (update->flags & REF_NEEDS_COMMIT) { + if (update->flags & REF_NEEDS_COMMIT || + update->flags & REF_LOG_ONLY) { if (commit_ref_update(update->lock, update->new_sha1, update->msg, update->flags, err)) { @@ -3168,7 +3171,8 @@ int ref_transaction_commit(struct ref_transaction *transaction, for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; - if (update->flags & REF_DELETING) { + if (update->flags & REF_DELETING && + !(update->flags & REF_LOG_ONLY)) { if (delete_ref_loose(update->lock, update->type, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 1f94f7a262..85f4650ab8 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -42,6 +42,13 @@ * value to ref_update::flags */ +/* + * Used as a flag in ref_update::flags when we want to log a ref + * update but not actually perform it. This is used when a symbolic + * ref update is split up. + */ +#define REF_LOG_ONLY 0x80 + /* * Return true iff refname is minimally safe. "Safe" here means that * deleting a loose reference by this name will not do any damage, for -- cgit v1.2.1 From 12fd3496d19c33c6401c5fdc7558944d46124a0f Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 24 Feb 2016 17:58:51 -0500 Subject: refs: don't dereference on rename When renaming refs, don't dereference either the origin or the destination before renaming. The origin does not need to be dereferenced because it is presently forbidden to rename symbolic refs. Not dereferencing the destination fixes a bug where renaming on top of a broken symref would use the pointed-to ref name for the moved reflog. Add a test for the reflog bug. Signed-off-by: David Turner Signed-off-by: Junio C Hamano Signed-off-by: Michael Haggerty --- refs/files-backend.c | 21 ++++++++++++++++----- t/t3200-branch.sh | 9 +++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 2f98eebe94..15ba456d68 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2333,7 +2333,8 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms if (log && S_ISLNK(loginfo.st_mode)) return error("reflog for %s is a symlink", oldrefname); - if (!resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING, orig_sha1, &flag)) + if (!resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, + orig_sha1, &flag)) return error("refname %s not found", oldrefname); if (flag & REF_ISSYMREF) @@ -2351,8 +2352,16 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms goto rollback; } - if (!read_ref_full(newrefname, RESOLVE_REF_READING, sha1, NULL) && - delete_ref(newrefname, sha1, REF_NODEREF)) { + /* + * Since we are doing a shallow lookup, sha1 is not the + * correct value to pass to delete_ref as old_sha1. But that + * doesn't matter, because an old_sha1 check wouldn't add to + * the safety anyway; we want to delete the reference whatever + * its current value. + */ + if (!read_ref_full(newrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, + sha1, NULL) && + delete_ref(newrefname, NULL, REF_NODEREF)) { if (errno==EISDIR) { struct strbuf path = STRBUF_INIT; int result; @@ -2376,7 +2385,8 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms logmoved = log; - lock = lock_ref_sha1_basic(newrefname, NULL, NULL, NULL, 0, NULL, &err); + lock = lock_ref_sha1_basic(newrefname, NULL, NULL, NULL, REF_NODEREF, + NULL, &err); if (!lock) { error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf); strbuf_release(&err); @@ -2394,7 +2404,8 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms return 0; rollback: - lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, NULL, 0, NULL, &err); + lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, NULL, REF_NODEREF, + NULL, &err); if (!lock) { error("unable to lock %s for rollback: %s", oldrefname, err.buf); strbuf_release(&err); diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index f3e3b6cf2e..42811604bb 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -79,6 +79,15 @@ test_expect_success 'git branch -m dumps usage' ' test_i18ngrep "branch name required" err ' +test_expect_success 'git branch -m m broken_symref should work' ' + test_when_finished "git branch -D broken_symref" && + git branch -l m && + git symbolic-ref refs/heads/broken_symref refs/heads/i_am_broken && + git branch -m m broken_symref && + git reflog exists refs/heads/broken_symref && + test_must_fail git reflog exists refs/heads/i_am_broken +' + test_expect_success 'git branch -m m m/m should work' ' git branch -l m && git branch -m m m/m && -- cgit v1.2.1 From 3a8af7be8f977cbf393dc77884a9ee6dfd611d95 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 25 Apr 2016 11:20:08 +0200 Subject: verify_refname_available(): adjust constness in declaration The two string_list arguments can be const. Signed-off-by: Michael Haggerty --- refs/files-backend.c | 4 ++-- refs/refs-internal.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 15ba456d68..1d1b72d57d 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2299,8 +2299,8 @@ out: } int verify_refname_available(const char *newname, - struct string_list *extras, - struct string_list *skip, + const struct string_list *extras, + const struct string_list *skip, struct strbuf *err) { struct ref_dir *packed_refs = get_packed_refs(&ref_cache); diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 85f4650ab8..9686e60942 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -116,8 +116,8 @@ enum peel_status peel_object(const unsigned char *name, unsigned char *sha1); * extras and skip must be sorted. */ int verify_refname_available(const char *newname, - struct string_list *extras, - struct string_list *skip, + const struct string_list *extras, + const struct string_list *skip, struct strbuf *err); /* -- cgit v1.2.1 From 71564516deccafba0a58129bd7d3851e28fdb4bb Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 25 Apr 2016 11:39:54 +0200 Subject: add_update(): initialize the whole ref_update Change add_update() to initialize all of the fields in the new ref_update object. Rename the function to ref_transaction_add_update(), and increase its visibility to all of the refs-related code. All of this makes the function more useful for other future callers. Signed-off-by: Michael Haggerty --- refs.c | 48 ++++++++++++++++++++++++++---------------------- refs/refs-internal.h | 14 ++++++++++++++ 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/refs.c b/refs.c index 5dc2473fbb..7c4eeb1c99 100644 --- a/refs.c +++ b/refs.c @@ -766,13 +766,33 @@ void ref_transaction_free(struct ref_transaction *transaction) free(transaction); } -static struct ref_update *add_update(struct ref_transaction *transaction, - const char *refname) +struct ref_update *ref_transaction_add_update( + struct ref_transaction *transaction, + const char *refname, unsigned int flags, + const unsigned char *new_sha1, + const unsigned char *old_sha1, + const char *msg) { struct ref_update *update; + + if (transaction->state != REF_TRANSACTION_OPEN) + die("BUG: update called for transaction that is not open"); + + if ((flags & REF_ISPRUNING) && !(flags & REF_NODEREF)) + die("BUG: REF_ISPRUNING set without REF_NODEREF"); + FLEX_ALLOC_STR(update, refname, refname); ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc); transaction->updates[transaction->nr++] = update; + + update->flags = flags; + + if (flags & REF_HAVE_NEW) + hashcpy(update->new_sha1, new_sha1); + if (flags & REF_HAVE_OLD) + hashcpy(update->old_sha1, old_sha1); + if (msg) + update->msg = xstrdup(msg); return update; } @@ -783,16 +803,8 @@ int ref_transaction_update(struct ref_transaction *transaction, unsigned int flags, const char *msg, struct strbuf *err) { - struct ref_update *update; - assert(err); - if (transaction->state != REF_TRANSACTION_OPEN) - die("BUG: update called for transaction that is not open"); - - if ((flags & REF_ISPRUNING) && !(flags & REF_NODEREF)) - die("BUG: REF_ISPRUNING set without REF_NODEREF"); - if (new_sha1 && !is_null_sha1(new_sha1) && check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { strbuf_addf(err, "refusing to update ref with bad name '%s'", @@ -800,18 +812,10 @@ int ref_transaction_update(struct ref_transaction *transaction, return -1; } - update = add_update(transaction, refname); - if (new_sha1) { - hashcpy(update->new_sha1, new_sha1); - flags |= REF_HAVE_NEW; - } - if (old_sha1) { - hashcpy(update->old_sha1, old_sha1); - flags |= REF_HAVE_OLD; - } - update->flags = flags; - if (msg) - update->msg = xstrdup(msg); + flags |= (new_sha1 ? REF_HAVE_NEW : 0) | (old_sha1 ? REF_HAVE_OLD : 0); + + ref_transaction_add_update(transaction, refname, flags, + new_sha1, old_sha1, msg); return 0; } diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 9686e60942..babdf2769f 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -157,6 +157,20 @@ struct ref_update { const char refname[FLEX_ARRAY]; }; +/* + * Add a ref_update with the specified properties to transaction, and + * return a pointer to the new object. This function does not verify + * that refname is well-formed. new_sha1 and old_sha1 are only + * dereferenced if the REF_HAVE_NEW and REF_HAVE_OLD bits, + * respectively, are set in flags. + */ +struct ref_update *ref_transaction_add_update( + struct ref_transaction *transaction, + const char *refname, unsigned int flags, + const unsigned char *new_sha1, + const unsigned char *old_sha1, + const char *msg); + /* * Transaction states. * OPEN: The transaction is in a valid state and can accept new updates. -- cgit v1.2.1 From 165056b2fc065e27e4077a11ed2bf1589207b997 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Sun, 24 Apr 2016 08:58:41 +0200 Subject: lock_ref_for_update(): new function Extract a new function, lock_ref_for_update(), from ref_transaction_commit(). Signed-off-by: Michael Haggerty --- refs/files-backend.c | 152 ++++++++++++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 67 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 1d1b72d57d..85751977f6 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -3051,6 +3051,88 @@ static int ref_update_reject_duplicates(struct string_list *refnames, return 0; } +/* + * Acquire all locks, verify old values if provided, check + * that new values are valid, and write new values to the + * lockfiles, ready to be activated. Only keep one lockfile + * open at a time to avoid running out of file descriptors. + */ +static int lock_ref_for_update(struct ref_update *update, + struct ref_transaction *transaction, + struct string_list *affected_refnames, + struct strbuf *err) +{ + int ret; + + if ((update->flags & REF_HAVE_NEW) && + is_null_sha1(update->new_sha1)) + update->flags |= REF_DELETING; + update->lock = lock_ref_sha1_basic( + update->refname, + ((update->flags & REF_HAVE_OLD) ? + update->old_sha1 : NULL), + affected_refnames, NULL, + update->flags, + &update->type, + err); + if (!update->lock) { + char *reason; + + ret = (errno == ENOTDIR) + ? TRANSACTION_NAME_CONFLICT + : TRANSACTION_GENERIC_ERROR; + reason = strbuf_detach(err, NULL); + strbuf_addf(err, "cannot lock ref '%s': %s", + update->refname, reason); + free(reason); + return ret; + } + if ((update->flags & REF_HAVE_NEW) && + !(update->flags & REF_DELETING) && + !(update->flags & REF_LOG_ONLY)) { + int overwriting_symref = ((update->type & REF_ISSYMREF) && + (update->flags & REF_NODEREF)); + + if (!overwriting_symref && + !hashcmp(update->lock->old_oid.hash, update->new_sha1)) { + /* + * The reference already has the desired + * value, so we don't need to write it. + */ + } else if (write_ref_to_lockfile(update->lock, + update->new_sha1, + err)) { + char *write_err = strbuf_detach(err, NULL); + + /* + * The lock was freed upon failure of + * write_ref_to_lockfile(): + */ + update->lock = NULL; + strbuf_addf(err, + "cannot update the ref '%s': %s", + update->refname, write_err); + free(write_err); + return TRANSACTION_GENERIC_ERROR; + } else { + update->flags |= REF_NEEDS_COMMIT; + } + } + if (!(update->flags & REF_NEEDS_COMMIT)) { + /* + * We didn't call write_ref_to_lockfile(), so + * the lockfile is still open. Close it to + * free up the file descriptor: + */ + if (close_ref(update->lock)) { + strbuf_addf(err, "couldn't close '%s.lock'", + update->refname); + return TRANSACTION_GENERIC_ERROR; + } + } + return 0; +} + int ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { @@ -3088,74 +3170,10 @@ int ref_transaction_commit(struct ref_transaction *transaction, for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; - if ((update->flags & REF_HAVE_NEW) && - is_null_sha1(update->new_sha1)) - update->flags |= REF_DELETING; - update->lock = lock_ref_sha1_basic( - update->refname, - ((update->flags & REF_HAVE_OLD) ? - update->old_sha1 : NULL), - &affected_refnames, NULL, - update->flags, - &update->type, - err); - if (!update->lock) { - char *reason; - - ret = (errno == ENOTDIR) - ? TRANSACTION_NAME_CONFLICT - : TRANSACTION_GENERIC_ERROR; - reason = strbuf_detach(err, NULL); - strbuf_addf(err, "cannot lock ref '%s': %s", - update->refname, reason); - free(reason); + ret = lock_ref_for_update(update, transaction, + &affected_refnames, err); + if (ret) goto cleanup; - } - if ((update->flags & REF_HAVE_NEW) && - !(update->flags & REF_DELETING) && - !(update->flags & REF_LOG_ONLY)) { - int overwriting_symref = ((update->type & REF_ISSYMREF) && - (update->flags & REF_NODEREF)); - - if (!overwriting_symref && - !hashcmp(update->lock->old_oid.hash, update->new_sha1)) { - /* - * The reference already has the desired - * value, so we don't need to write it. - */ - } else if (write_ref_to_lockfile(update->lock, - update->new_sha1, - err)) { - char *write_err = strbuf_detach(err, NULL); - - /* - * The lock was freed upon failure of - * write_ref_to_lockfile(): - */ - update->lock = NULL; - strbuf_addf(err, - "cannot update the ref '%s': %s", - update->refname, write_err); - free(write_err); - ret = TRANSACTION_GENERIC_ERROR; - goto cleanup; - } else { - update->flags |= REF_NEEDS_COMMIT; - } - } - if (!(update->flags & REF_NEEDS_COMMIT)) { - /* - * We didn't call write_ref_to_lockfile(), so - * the lockfile is still open. Close it to - * free up the file descriptor: - */ - if (close_ref(update->lock)) { - strbuf_addf(err, "couldn't close '%s.lock'", - update->refname); - ret = TRANSACTION_GENERIC_ERROR; - goto cleanup; - } - } } /* Perform updates first so live commits remain referenced */ -- cgit v1.2.1 From 8415d24746b97a479fe5aec9845bfc150cda2d14 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Sun, 24 Apr 2016 08:11:37 +0200 Subject: unlock_ref(): move definition higher in the file This avoids the need for a forward declaration in the next patch. Signed-off-by: Michael Haggerty --- refs/files-backend.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 85751977f6..dc0bde05b8 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1516,6 +1516,16 @@ out: return ret; } +static void unlock_ref(struct ref_lock *lock) +{ + /* Do not free lock->lk -- atexit() still looks at them */ + if (lock->lk) + rollback_lock_file(lock->lk); + free(lock->ref_name); + free(lock->orig_ref_name); + free(lock); +} + /* * Peel the entry (if possible) and return its new peel_status. If * repeel is true, re-peel the entry even if there is an old peeled @@ -1674,16 +1684,6 @@ int do_for_each_ref(const char *submodule, const char *base, return do_for_each_entry(refs, base, do_one_ref, &data); } -static void unlock_ref(struct ref_lock *lock) -{ - /* Do not free lock->lk -- atexit() still looks at them */ - if (lock->lk) - rollback_lock_file(lock->lk); - free(lock->ref_name); - free(lock->orig_ref_name); - free(lock); -} - /* * Verify that the reference locked by lock has the value old_sha1. * Fail if the reference doesn't exist and mustexist is set. Return 0 -- cgit v1.2.1 From 8a679de6f1a4bd077f828273f75eea46947b5b73 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Wed, 27 Apr 2016 15:54:45 +0200 Subject: ref_transaction_update(): check refname_is_safe() at a minimum If the user has asked that a new value be set for a reference, we use check_refname_format() to verify that the reference name satisfies all of the rules. But in other cases, at least check that refname_is_safe(). Signed-off-by: Michael Haggerty --- refs.c | 5 +++-- t/t1400-update-ref.sh | 2 +- t/t1430-bad-ref-name.sh | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/refs.c b/refs.c index 7c4eeb1c99..842c5c7b05 100644 --- a/refs.c +++ b/refs.c @@ -805,8 +805,9 @@ int ref_transaction_update(struct ref_transaction *transaction, { assert(err); - if (new_sha1 && !is_null_sha1(new_sha1) && - check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { + if ((new_sha1 && !is_null_sha1(new_sha1)) ? + check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) : + !refname_is_safe(refname)) { strbuf_addf(err, "refusing to update ref with bad name '%s'", refname); return -1; diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 40b0ccedfc..08bd8fd8d6 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -23,7 +23,7 @@ test_expect_success setup ' m=refs/heads/master n_dir=refs/heads/gu n=$n_dir/fixes -outside=foo +outside=refs/foo test_expect_success \ "create $m" \ diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh index 25ddab4e98..8937e25e49 100755 --- a/t/t1430-bad-ref-name.sh +++ b/t/t1430-bad-ref-name.sh @@ -285,7 +285,7 @@ test_expect_success 'update-ref -d cannot delete non-ref in .git dir' ' echo precious >expect && test_must_fail git update-ref -d my-private-file >output 2>error && test_must_be_empty output && - test_i18ngrep -e "cannot lock .*: unable to resolve reference" error && + test_i18ngrep -e "refusing to update ref with bad name" error && test_cmp expect .git/my-private-file ' -- cgit v1.2.1 From 92b1551b1d407065f961ffd1d972481063a0edcc Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 25 Apr 2016 15:56:07 +0200 Subject: refs: resolve symbolic refs first Before committing ref updates, split symbolic ref updates into two parts: an update to the underlying ref, and a log-only update to the symbolic ref. This ensures that both references are locked correctly during the transaction, including while their reflogs are updated. Similarly, if the reference pointed to by HEAD is modified directly, add a separate log-only update to HEAD, rather than leaving the job of updating HEAD's reflog to commit_ref_update(). This change ensures that HEAD is locked correctly while its reflog is being modified, as well as being cheaper (HEAD only needs to be resolved once). This makes use of a new function, lock_raw_ref(), which is analogous to read_raw_ref(), but acquires a lock on the reference before reading it. This change still has two problems: * There are redundant read_ref_full() reference lookups. * It is still possible to get incorrect reflogs for symbolic references if there is a concurrent update by another process, since the old_oid of a symref is determined before the lock on the pointed-to ref is held. Both problems will soon be fixed. Signed-off-by: David Turner Signed-off-by: Junio C Hamano Signed-off-by: Michael Haggerty WIP --- refs/files-backend.c | 508 ++++++++++++++++++++++++++++++++++++++++++++++---- refs/refs-internal.h | 11 +- t/t1400-update-ref.sh | 35 ++++ 3 files changed, 514 insertions(+), 40 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index dc0bde05b8..ffc30fd232 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1526,6 +1526,233 @@ static void unlock_ref(struct ref_lock *lock) free(lock); } +/* + * Lock refname, without following symrefs, and set *lock_p to point + * at a newly-allocated lock object. Fill in lock->old_oid, referent, + * and type similarly to read_raw_ref(). + * + * The caller must verify that refname is a "safe" reference name (in + * the sense of refname_is_safe()) before calling this function. + * + * If the reference doesn't already exist, verify that refname doesn't + * have a D/F conflict with any existing references. extras and skip + * are passed to verify_refname_available_dir() for this check. + * + * If mustexist is not set and the reference is not found or is + * broken, lock the reference anyway but clear sha1. + * + * Return 0 on success. On failure, write an error message to err and + * return TRANSACTION_NAME_CONFLICT or TRANSACTION_GENERIC_ERROR. + * + * Implementation note: This function is basically + * + * lock reference + * read_raw_ref() + * + * but it includes a lot more code to + * - Deal with possible races with other processes + * - Avoid calling verify_refname_available_dir() when it can be + * avoided, namely if we were successfully able to read the ref + * - Generate informative error messages in the case of failure + */ +static int lock_raw_ref(const char *refname, int mustexist, + const struct string_list *extras, + const struct string_list *skip, + struct ref_lock **lock_p, + struct strbuf *referent, + unsigned int *type, + struct strbuf *err) +{ + struct ref_lock *lock; + struct strbuf ref_file = STRBUF_INIT; + int attempts_remaining = 3; + int ret = TRANSACTION_GENERIC_ERROR; + + assert(err); + *type = 0; + + /* First lock the file so it can't change out from under us. */ + + *lock_p = lock = xcalloc(1, sizeof(*lock)); + + lock->ref_name = xstrdup(refname); + lock->orig_ref_name = xstrdup(refname); + strbuf_git_path(&ref_file, "%s", refname); + +retry: + switch (safe_create_leading_directories(ref_file.buf)) { + case SCLD_OK: + break; /* success */ + case SCLD_EXISTS: + /* + * Suppose refname is "refs/foo/bar". We just failed + * to create the containing directory, "refs/foo", + * because there was a non-directory in the way. This + * indicates a D/F conflict, probably because of + * another reference such as "refs/foo". There is no + * reason to expect this error to be transitory. + */ + if (verify_refname_available(refname, extras, skip, err)) { + if (mustexist) { + /* + * To the user the relevant error is + * that the "mustexist" reference is + * missing: + */ + strbuf_reset(err); + strbuf_addf(err, "unable to resolve reference '%s'", + refname); + } else { + /* + * The error message set by + * verify_refname_available_dir() is OK. + */ + ret = TRANSACTION_NAME_CONFLICT; + } + } else { + /* + * The file that is in the way isn't a loose + * reference. Report it as a low-level + * failure. + */ + strbuf_addf(err, "unable to create lock file %s.lock; " + "non-directory in the way", + ref_file.buf); + } + goto error_return; + case SCLD_VANISHED: + /* Maybe another process was tidying up. Try again. */ + if (--attempts_remaining > 0) + goto retry; + /* fall through */ + default: + strbuf_addf(err, "unable to create directory for %s", + ref_file.buf); + goto error_return; + } + + if (!lock->lk) + lock->lk = xcalloc(1, sizeof(struct lock_file)); + + if (hold_lock_file_for_update(lock->lk, ref_file.buf, LOCK_NO_DEREF) < 0) { + if (errno == ENOENT && --attempts_remaining > 0) { + /* + * Maybe somebody just deleted one of the + * directories leading to ref_file. Try + * again: + */ + goto retry; + } else { + unable_to_lock_message(ref_file.buf, errno, err); + goto error_return; + } + } + + /* + * Now we hold the lock and can read the reference without + * fear that its value will change. + */ + + if (read_raw_ref(refname, lock->old_oid.hash, referent, type)) { + if (errno == ENOENT) { + if (mustexist) { + /* Garden variety missing reference. */ + strbuf_addf(err, "unable to resolve reference '%s'", + refname); + goto error_return; + } else { + /* + * Reference is missing, but that's OK. We + * know that there is not a conflict with + * another loose reference because + * (supposing that we are trying to lock + * reference "refs/foo/bar"): + * + * - We were successfully able to create + * the lockfile refs/foo/bar.lock, so we + * know there cannot be a loose reference + * named "refs/foo". + * + * - We got ENOENT and not EISDIR, so we + * know that there cannot be a loose + * reference named "refs/foo/bar/baz". + */ + } + } else if (errno == EISDIR) { + /* + * There is a directory in the way. It might have + * contained references that have been deleted. If + * we don't require that the reference already + * exists, try to remove the directory so that it + * doesn't cause trouble when we want to rename the + * lockfile into place later. + */ + if (mustexist) { + /* Garden variety missing reference. */ + strbuf_addf(err, "unable to resolve reference '%s'", + refname); + goto error_return; + } else if (remove_dir_recursively(&ref_file, + REMOVE_DIR_EMPTY_ONLY)) { + if (verify_refname_available_dir( + refname, extras, skip, + get_loose_refs(&ref_cache), + err)) { + /* + * The error message set by + * verify_refname_available() is OK. + */ + ret = TRANSACTION_NAME_CONFLICT; + goto error_return; + } else { + /* + * We can't delete the directory, + * but we also don't know of any + * references that it should + * contain. + */ + strbuf_addf(err, "there is a non-empty directory '%s' " + "blocking reference '%s'", + ref_file.buf, refname); + goto error_return; + } + } + } else if (errno == EINVAL && (*type & REF_ISBROKEN)) { + strbuf_addf(err, "unable to resolve reference '%s': " + "reference broken", refname); + goto error_return; + } else { + strbuf_addf(err, "unable to resolve reference '%s': %s", + refname, strerror(errno)); + goto error_return; + } + + /* + * If the ref did not exist and we are creating it, + * make sure there is no existing packed ref whose + * name begins with our refname, nor a packed ref + * whose name is a proper prefix of our refname. + */ + if (verify_refname_available_dir( + refname, extras, skip, + get_packed_refs(&ref_cache), + err)) { + goto error_return; + } + } + + ret = 0; + goto out; + +error_return: + unlock_ref(lock); + *lock_p = NULL; + +out: + strbuf_release(&ref_file); + return ret; +} + /* * Peel the entry (if possible) and return its new peel_status. If * repeel is true, re-peel the entry even if there is an old peeled @@ -3052,55 +3279,202 @@ static int ref_update_reject_duplicates(struct string_list *refnames, } /* - * Acquire all locks, verify old values if provided, check - * that new values are valid, and write new values to the - * lockfiles, ready to be activated. Only keep one lockfile - * open at a time to avoid running out of file descriptors. + * If update is a direct update of head_ref (the reference pointed to + * by HEAD), then add an extra REF_LOG_ONLY update for HEAD. + */ +static int split_head_update(struct ref_update *update, + struct ref_transaction *transaction, + const char *head_ref, + struct string_list *affected_refnames, + struct strbuf *err) +{ + struct string_list_item *item; + struct ref_update *new_update; + + if ((update->flags & REF_LOG_ONLY) || + (update->flags & REF_ISPRUNING) || + (update->flags & REF_UPDATE_VIA_HEAD)) + return 0; + + if (strcmp(update->refname, head_ref)) + return 0; + + /* + * First make sure that HEAD is not already in the + * transaction. This insertion is O(N) in the transaction + * size, but it happens at most once per transaction. + */ + item = string_list_insert(affected_refnames, "HEAD"); + if (item->util) { + /* An entry already existed */ + strbuf_addf(err, + "multiple updates for 'HEAD' (including one " + "via its referent '%s') are not allowed", + update->refname); + return TRANSACTION_NAME_CONFLICT; + } + + new_update = ref_transaction_add_update( + transaction, "HEAD", + update->flags | REF_LOG_ONLY | REF_NODEREF, + update->new_sha1, update->old_sha1, + update->msg); + + item->util = new_update; + + return 0; +} + +/* + * update is for a symref that points at referent and doesn't have + * REF_NODEREF set. Split it into two updates: + * - The original update, but with REF_LOG_ONLY and REF_NODEREF set + * - A new, separate update for the referent reference + * Note that the new update will itself be subject to splitting when + * the iteration gets to it. + */ +static int split_symref_update(struct ref_update *update, + const char *referent, + struct ref_transaction *transaction, + struct string_list *affected_refnames, + struct strbuf *err) +{ + struct string_list_item *item; + struct ref_update *new_update; + unsigned int new_flags; + + /* + * First make sure that referent is not already in the + * transaction. This insertion is O(N) in the transaction + * size, but it happens at most once per symref in a + * transaction. + */ + item = string_list_insert(affected_refnames, referent); + if (item->util) { + /* An entry already existed */ + strbuf_addf(err, + "multiple updates for '%s' (including one " + "via symref '%s') are not allowed", + referent, update->refname); + return TRANSACTION_NAME_CONFLICT; + } + + new_flags = update->flags; + if (!strcmp(update->refname, "HEAD")) { + /* + * Record that the new update came via HEAD, so that + * when we process it, split_head_update() doesn't try + * to add another reflog update for HEAD. Note that + * this bit will be propagated if the new_update + * itself needs to be split. + */ + new_flags |= REF_UPDATE_VIA_HEAD; + } + + new_update = ref_transaction_add_update( + transaction, referent, new_flags, + update->new_sha1, update->old_sha1, + update->msg); + + /* Change the symbolic ref update to log only: */ + update->flags |= REF_LOG_ONLY | REF_NODEREF; + + item->util = new_update; + + return 0; +} + +/* + * Prepare for carrying out update: + * - Lock the reference referred to by update. + * - Read the reference under lock. + * - Check that its old SHA-1 value (if specified) is correct, and in + * any case record it in update->lock->old_oid for later use when + * writing the reflog. + * - If it is a symref update without REF_NODEREF, split it up into a + * REF_LOG_ONLY update of the symref and add a separate update for + * the referent to transaction. + * - If it is an update of head_ref, add a corresponding REF_LOG_ONLY + * update of HEAD. */ static int lock_ref_for_update(struct ref_update *update, struct ref_transaction *transaction, + const char *head_ref, struct string_list *affected_refnames, struct strbuf *err) { + struct strbuf referent = STRBUF_INIT; + int mustexist = (update->flags & REF_HAVE_OLD) && + !is_null_sha1(update->old_sha1); int ret; + struct ref_lock *lock; - if ((update->flags & REF_HAVE_NEW) && - is_null_sha1(update->new_sha1)) + if ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1)) update->flags |= REF_DELETING; - update->lock = lock_ref_sha1_basic( - update->refname, - ((update->flags & REF_HAVE_OLD) ? - update->old_sha1 : NULL), - affected_refnames, NULL, - update->flags, - &update->type, - err); - if (!update->lock) { + + if (head_ref) { + ret = split_head_update(update, transaction, head_ref, + affected_refnames, err); + if (ret) + return ret; + } + + ret = lock_raw_ref(update->refname, mustexist, + affected_refnames, NULL, + &update->lock, &referent, + &update->type, err); + + if (ret) { char *reason; - ret = (errno == ENOTDIR) - ? TRANSACTION_NAME_CONFLICT - : TRANSACTION_GENERIC_ERROR; reason = strbuf_detach(err, NULL); strbuf_addf(err, "cannot lock ref '%s': %s", update->refname, reason); free(reason); return ret; } + + lock = update->lock; + + if (read_ref_full(update->refname, + mustexist ? RESOLVE_REF_READING : 0, + lock->old_oid.hash, NULL)) { + if (update->flags & REF_HAVE_OLD) { + strbuf_addf(err, "cannot lock ref '%s': can't resolve old value", + update->refname); + return TRANSACTION_GENERIC_ERROR; + } else { + hashclr(lock->old_oid.hash); + } + } + if ((update->flags & REF_HAVE_OLD) && + hashcmp(lock->old_oid.hash, update->old_sha1)) { + strbuf_addf(err, "cannot lock ref '%s': is at %s but expected %s", + update->refname, + sha1_to_hex(lock->old_oid.hash), + sha1_to_hex(update->old_sha1)); + return TRANSACTION_GENERIC_ERROR; + } + + if (update->type & REF_ISSYMREF) { + if (!(update->flags & REF_NODEREF)) { + ret = split_symref_update(update, referent.buf, transaction, + affected_refnames, err); + if (ret) + return ret; + } + } + if ((update->flags & REF_HAVE_NEW) && !(update->flags & REF_DELETING) && !(update->flags & REF_LOG_ONLY)) { - int overwriting_symref = ((update->type & REF_ISSYMREF) && - (update->flags & REF_NODEREF)); - - if (!overwriting_symref && - !hashcmp(update->lock->old_oid.hash, update->new_sha1)) { + if (!(update->type & REF_ISSYMREF) && + !hashcmp(lock->old_oid.hash, update->new_sha1)) { /* * The reference already has the desired * value, so we don't need to write it. */ - } else if (write_ref_to_lockfile(update->lock, - update->new_sha1, + } else if (write_ref_to_lockfile(lock, update->new_sha1, err)) { char *write_err = strbuf_detach(err, NULL); @@ -3124,7 +3498,7 @@ static int lock_ref_for_update(struct ref_update *update, * the lockfile is still open. Close it to * free up the file descriptor: */ - if (close_ref(update->lock)) { + if (close_ref(lock)) { strbuf_addf(err, "couldn't close '%s.lock'", update->refname); return TRANSACTION_GENERIC_ERROR; @@ -3140,6 +3514,9 @@ int ref_transaction_commit(struct ref_transaction *transaction, struct string_list refs_to_delete = STRING_LIST_INIT_NODUP; struct string_list_item *ref_to_delete; struct string_list affected_refnames = STRING_LIST_INIT_NODUP; + char *head_ref = NULL; + int head_type; + struct object_id head_oid; assert(err); @@ -3151,16 +3528,57 @@ int ref_transaction_commit(struct ref_transaction *transaction, return 0; } - /* Fail if a refname appears more than once in the transaction: */ - for (i = 0; i < transaction->nr; i++) - string_list_append(&affected_refnames, - transaction->updates[i]->refname); + /* + * Fail if a refname appears more than once in the + * transaction. (If we end up splitting up any updates using + * split_symref_update() or split_head_update(), those + * functions will check that the new updates don't have the + * same refname as any existing ones.) + */ + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + struct string_list_item *item = + string_list_append(&affected_refnames, update->refname); + + /* + * We store a pointer to update in item->util, but at + * the moment we never use the value of this field + * except to check whether it is non-NULL. + */ + item->util = update; + } string_list_sort(&affected_refnames); if (ref_update_reject_duplicates(&affected_refnames, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } + /* + * Special hack: If a branch is updated directly and HEAD + * points to it (may happen on the remote side of a push + * for example) then logically the HEAD reflog should be + * updated too. + * + * A generic solution would require reverse symref lookups, + * but finding all symrefs pointing to a given branch would be + * rather costly for this rare event (the direct update of a + * branch) to be worth it. So let's cheat and check with HEAD + * only, which should cover 99% of all usage scenarios (even + * 100% of the default ones). + * + * So if HEAD is a symbolic reference, then record the name of + * the reference that it points to. If we see an update of + * head_ref within the transaction, then split_head_update() + * arranges for the reflog of HEAD to be updated, too. + */ + head_ref = resolve_refdup("HEAD", RESOLVE_REF_NO_RECURSE, + head_oid.hash, &head_type); + + if (head_ref && !(head_type & REF_ISSYMREF)) { + free(head_ref); + head_ref = NULL; + } + /* * Acquire all locks, verify old values if provided, check * that new values are valid, and write new values to the @@ -3170,7 +3588,7 @@ int ref_transaction_commit(struct ref_transaction *transaction, for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; - ret = lock_ref_for_update(update, transaction, + ret = lock_ref_for_update(update, transaction, head_ref, &affected_refnames, err); if (ret) goto cleanup; @@ -3179,23 +3597,35 @@ int ref_transaction_commit(struct ref_transaction *transaction, /* Perform updates first so live commits remain referenced */ for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; + struct ref_lock *lock = update->lock; if (update->flags & REF_NEEDS_COMMIT || update->flags & REF_LOG_ONLY) { - if (commit_ref_update(update->lock, - update->new_sha1, update->msg, - update->flags, err)) { - /* freed by commit_ref_update(): */ + if (log_ref_write(lock->ref_name, lock->old_oid.hash, + update->new_sha1, + update->msg, update->flags, err)) { + char *old_msg = strbuf_detach(err, NULL); + + strbuf_addf(err, "cannot update the ref '%s': %s", + lock->ref_name, old_msg); + free(old_msg); + unlock_ref(lock); update->lock = NULL; ret = TRANSACTION_GENERIC_ERROR; goto cleanup; - } else { - /* freed by commit_ref_update(): */ + } + } + if (update->flags & REF_NEEDS_COMMIT) { + clear_loose_ref_cache(&ref_cache); + if (commit_ref(lock)) { + strbuf_addf(err, "couldn't set '%s'", lock->ref_name); + unlock_ref(lock); update->lock = NULL; + ret = TRANSACTION_GENERIC_ERROR; + goto cleanup; } } } - /* Perform deletes now that updates are safely completed */ for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; @@ -3228,7 +3658,9 @@ cleanup: if (transaction->updates[i]->lock) unlock_ref(transaction->updates[i]->lock); string_list_clear(&refs_to_delete, 0); + free(head_ref); string_list_clear(&affected_refnames, 0); + return ret; } diff --git a/refs/refs-internal.h b/refs/refs-internal.h index babdf2769f..cccd76b28c 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -49,6 +49,12 @@ */ #define REF_LOG_ONLY 0x80 +/* + * Internal flag, meaning that the containing ref_update was via an + * update to HEAD. + */ +#define REF_UPDATE_VIA_HEAD 0x100 + /* * Return true iff refname is minimally safe. "Safe" here means that * deleting a loose reference by this name will not do any damage, for @@ -148,11 +154,12 @@ struct ref_update { unsigned char old_sha1[20]; /* * One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF, - * REF_DELETING, and REF_ISPRUNING: + * REF_DELETING, REF_ISPRUNING, REF_LOG_ONLY, and + * REF_UPDATE_VIA_HEAD: */ unsigned int flags; struct ref_lock *lock; - int type; + unsigned int type; char *msg; const char refname[FLEX_ARRAY]; }; diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 08bd8fd8d6..d226930412 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -1102,6 +1102,41 @@ test_expect_success 'stdin -z delete refs works with packed and loose refs' ' test_must_fail git rev-parse --verify -q $c ' +test_expect_success 'fails with duplicate HEAD update' ' + git branch target1 $A && + git checkout target1 && + cat >stdin <<-EOF && + update refs/heads/target1 $C + option no-deref + update HEAD $B + EOF + test_must_fail git update-ref --stdin err && + grep "fatal: multiple updates for '\''HEAD'\'' (including one via its referent .refs/heads/target1.) are not allowed" err && + echo "refs/heads/target1" >expect && + git symbolic-ref HEAD >actual && + test_cmp expect actual && + echo "$A" >expect && + git rev-parse refs/heads/target1 >actual && + test_cmp expect actual +' + +test_expect_success 'fails with duplicate ref update via symref' ' + git branch target2 $A && + git symbolic-ref refs/heads/symref2 refs/heads/target2 && + cat >stdin <<-EOF && + update refs/heads/target2 $C + update refs/heads/symref2 $B + EOF + test_must_fail git update-ref --stdin err && + grep "fatal: multiple updates for '\''refs/heads/target2'\'' (including one via symref .refs/heads/symref2.) are not allowed" err && + echo "refs/heads/target2" >expect && + git symbolic-ref refs/heads/symref2 >actual && + test_cmp expect actual && + echo "$A" >expect && + git rev-parse refs/heads/target2 >actual && + test_cmp expect actual +' + run_with_limited_open_files () { (ulimit -n 32 && "$@") } -- cgit v1.2.1 From 8169d0d06ad721aa54d95f044f4b097d79151ea2 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 25 Apr 2016 17:38:35 +0200 Subject: lock_ref_for_update(): don't re-read non-symbolic references Before the previous patch, our first read of the reference happened before the reference was locked, so we couldn't trust its value and had to read it again. But now that our first read of the reference happens after acquiring the lock, there is no need to read it a second time. So move the read_ref_full() call into the (update->type & REF_ISSYMREF) block. Signed-off-by: Michael Haggerty --- refs/files-backend.c | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index ffc30fd232..7bc18322aa 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -3436,33 +3436,45 @@ static int lock_ref_for_update(struct ref_update *update, lock = update->lock; - if (read_ref_full(update->refname, - mustexist ? RESOLVE_REF_READING : 0, - lock->old_oid.hash, NULL)) { - if (update->flags & REF_HAVE_OLD) { - strbuf_addf(err, "cannot lock ref '%s': can't resolve old value", - update->refname); + if (update->type & REF_ISSYMREF) { + if (read_ref_full(update->refname, + mustexist ? RESOLVE_REF_READING : 0, + lock->old_oid.hash, NULL)) { + if (update->flags & REF_HAVE_OLD) { + strbuf_addf(err, "cannot lock ref '%s': can't resolve old value", + update->refname); + return TRANSACTION_GENERIC_ERROR; + } else { + hashclr(lock->old_oid.hash); + } + } + if ((update->flags & REF_HAVE_OLD) && + hashcmp(lock->old_oid.hash, update->old_sha1)) { + strbuf_addf(err, "cannot lock ref '%s': is at %s but expected %s", + update->refname, + sha1_to_hex(lock->old_oid.hash), + sha1_to_hex(update->old_sha1)); return TRANSACTION_GENERIC_ERROR; - } else { - hashclr(lock->old_oid.hash); } - } - if ((update->flags & REF_HAVE_OLD) && - hashcmp(lock->old_oid.hash, update->old_sha1)) { - strbuf_addf(err, "cannot lock ref '%s': is at %s but expected %s", - update->refname, - sha1_to_hex(lock->old_oid.hash), - sha1_to_hex(update->old_sha1)); - return TRANSACTION_GENERIC_ERROR; - } - if (update->type & REF_ISSYMREF) { if (!(update->flags & REF_NODEREF)) { ret = split_symref_update(update, referent.buf, transaction, affected_refnames, err); if (ret) return ret; } + } else if ((update->flags & REF_HAVE_OLD) && + hashcmp(lock->old_oid.hash, update->old_sha1)) { + if (is_null_sha1(update->old_sha1)) + strbuf_addf(err, "cannot lock ref '%s': reference already exists", + update->refname); + else + strbuf_addf(err, "cannot lock ref '%s': is at %s but expected %s", + update->refname, + sha1_to_hex(lock->old_oid.hash), + sha1_to_hex(update->old_sha1)); + + return TRANSACTION_GENERIC_ERROR; } if ((update->flags & REF_HAVE_NEW) && -- cgit v1.2.1 From 6e30b2f652d0a6748e2041dee5b5612cafca29b2 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Mon, 25 Apr 2016 17:48:32 +0200 Subject: lock_ref_for_update(): don't resolve symrefs If a transaction includes a non-NODEREF update to a symbolic reference, we don't have to look it up in lock_ref_for_update(). The reference will be dereferenced anyway when the split-off update is processed. This change requires that we store a backpointer from the split-off update to its parent update, for two reasons: * We still want to report the original reference name in error messages. So if an error occurs when checking the split-off update's old_sha1, walk the parent_update pointers back to find the original reference name, and report that one. * We still need to write the old_sha1 of the symref to its reflog. So after we read the split-off update's reference value, walk the parent_update pointers back and fill in their old_sha1 fields. Aside from eliminating unnecessary reads, this change fixes a subtle (though not very serious) race condition: in the old code, the old_sha1 of the symref was resolved before the reference that it pointed at was locked. So it was possible that the old_sha1 value logged to the symref's reflog could be wrong if another process changed the downstream reference before it was locked. Signed-off-by: Michael Haggerty --- refs/files-backend.c | 108 +++++++++++++++++++++++++++++++++++++-------------- refs/refs-internal.h | 17 ++++++++ 2 files changed, 95 insertions(+), 30 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 7bc18322aa..a783513435 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -3376,14 +3376,32 @@ static int split_symref_update(struct ref_update *update, update->new_sha1, update->old_sha1, update->msg); - /* Change the symbolic ref update to log only: */ + new_update->parent_update = update; + + /* + * Change the symbolic ref update to log only. Also, it + * doesn't need to check its old SHA-1 value, as that will be + * done when new_update is processed. + */ update->flags |= REF_LOG_ONLY | REF_NODEREF; + update->flags &= ~REF_HAVE_OLD; item->util = new_update; return 0; } +/* + * Return the refname under which update was originally requested. + */ +static const char *original_update_refname(struct ref_update *update) +{ + while (update->parent_update) + update = update->parent_update; + + return update->refname; +} + /* * Prepare for carrying out update: * - Lock the reference referred to by update. @@ -3437,44 +3455,74 @@ static int lock_ref_for_update(struct ref_update *update, lock = update->lock; if (update->type & REF_ISSYMREF) { - if (read_ref_full(update->refname, - mustexist ? RESOLVE_REF_READING : 0, - lock->old_oid.hash, NULL)) { - if (update->flags & REF_HAVE_OLD) { - strbuf_addf(err, "cannot lock ref '%s': can't resolve old value", - update->refname); + if (update->flags & REF_NODEREF) { + /* + * We won't be reading the referent as part of + * the transaction, so we have to read it here + * to record and possibly check old_sha1: + */ + if (read_ref_full(update->refname, + mustexist ? RESOLVE_REF_READING : 0, + lock->old_oid.hash, NULL)) { + if (update->flags & REF_HAVE_OLD) { + strbuf_addf(err, "cannot lock ref '%s': " + "can't resolve old value", + update->refname); + return TRANSACTION_GENERIC_ERROR; + } else { + hashclr(lock->old_oid.hash); + } + } + if ((update->flags & REF_HAVE_OLD) && + hashcmp(lock->old_oid.hash, update->old_sha1)) { + strbuf_addf(err, "cannot lock ref '%s': " + "is at %s but expected %s", + update->refname, + sha1_to_hex(lock->old_oid.hash), + sha1_to_hex(update->old_sha1)); return TRANSACTION_GENERIC_ERROR; - } else { - hashclr(lock->old_oid.hash); } - } - if ((update->flags & REF_HAVE_OLD) && - hashcmp(lock->old_oid.hash, update->old_sha1)) { - strbuf_addf(err, "cannot lock ref '%s': is at %s but expected %s", - update->refname, - sha1_to_hex(lock->old_oid.hash), - sha1_to_hex(update->old_sha1)); - return TRANSACTION_GENERIC_ERROR; - } - if (!(update->flags & REF_NODEREF)) { + } else { + /* + * Create a new update for the reference this + * symref is pointing at. Also, we will record + * and verify old_sha1 for this update as part + * of processing the split-off update, so we + * don't have to do it here. + */ ret = split_symref_update(update, referent.buf, transaction, affected_refnames, err); if (ret) return ret; } - } else if ((update->flags & REF_HAVE_OLD) && - hashcmp(lock->old_oid.hash, update->old_sha1)) { - if (is_null_sha1(update->old_sha1)) - strbuf_addf(err, "cannot lock ref '%s': reference already exists", - update->refname); - else - strbuf_addf(err, "cannot lock ref '%s': is at %s but expected %s", - update->refname, - sha1_to_hex(lock->old_oid.hash), - sha1_to_hex(update->old_sha1)); + } else { + struct ref_update *parent_update; + + /* + * If this update is happening indirectly because of a + * symref update, record the old SHA-1 in the parent + * update: + */ + for (parent_update = update->parent_update; + parent_update; + parent_update = parent_update->parent_update) { + oidcpy(&parent_update->lock->old_oid, &lock->old_oid); + } - return TRANSACTION_GENERIC_ERROR; + if ((update->flags & REF_HAVE_OLD) && + hashcmp(lock->old_oid.hash, update->old_sha1)) { + if (is_null_sha1(update->old_sha1)) + strbuf_addf(err, "cannot lock ref '%s': reference already exists", + original_update_refname(update)); + else + strbuf_addf(err, "cannot lock ref '%s': is at %s but expected %s", + original_update_refname(update), + sha1_to_hex(lock->old_oid.hash), + sha1_to_hex(update->old_sha1)); + + return TRANSACTION_GENERIC_ERROR; + } } if ((update->flags & REF_HAVE_NEW) && diff --git a/refs/refs-internal.h b/refs/refs-internal.h index cccd76b28c..1bb3d87dc5 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -143,24 +143,41 @@ int should_autocreate_reflog(const char *refname); * not exist before update. */ struct ref_update { + /* * If (flags & REF_HAVE_NEW), set the reference to this value: */ unsigned char new_sha1[20]; + /* * If (flags & REF_HAVE_OLD), check that the reference * previously had this value: */ unsigned char old_sha1[20]; + /* * One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF, * REF_DELETING, REF_ISPRUNING, REF_LOG_ONLY, and * REF_UPDATE_VIA_HEAD: */ unsigned int flags; + struct ref_lock *lock; unsigned int type; char *msg; + + /* + * If this ref_update was split off of a symref update via + * split_symref_update(), then this member points at that + * update. This is used for two purposes: + * 1. When reporting errors, we report the refname under which + * the update was originally requested. + * 2. When we read the old value of this reference, we + * propagate it back to its parent update for recording in + * the latter's reflog. + */ + struct ref_update *parent_update; + const char refname[FLEX_ARRAY]; }; -- cgit v1.2.1 From 5d9b2de4ef5a6b0cc38bbb3affcc614a66c663d7 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Fri, 22 Apr 2016 14:38:56 +0200 Subject: commit_ref_update(): remove the flags parameter commit_ref_update() is now only called with flags=0. So remove the flags parameter entirely. Signed-off-by: Michael Haggerty --- refs/files-backend.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index a783513435..7f9949352b 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2546,7 +2546,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock, const unsigned char *sha1, struct strbuf *err); static int commit_ref_update(struct ref_lock *lock, const unsigned char *sha1, const char *logmsg, - int flags, struct strbuf *err); + struct strbuf *err); int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg) { @@ -2622,7 +2622,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms hashcpy(lock->old_oid.hash, orig_sha1); if (write_ref_to_lockfile(lock, orig_sha1, &err) || - commit_ref_update(lock, orig_sha1, logmsg, 0, &err)) { + commit_ref_update(lock, orig_sha1, logmsg, &err)) { error("unable to write current sha1 into %s: %s", newrefname, err.buf); strbuf_release(&err); goto rollback; @@ -2642,7 +2642,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms flag = log_all_ref_updates; log_all_ref_updates = 0; if (write_ref_to_lockfile(lock, orig_sha1, &err) || - commit_ref_update(lock, orig_sha1, NULL, 0, &err)) { + commit_ref_update(lock, orig_sha1, NULL, &err)) { error("unable to write current sha1 into %s: %s", oldrefname, err.buf); strbuf_release(&err); } @@ -2880,12 +2880,12 @@ static int write_ref_to_lockfile(struct ref_lock *lock, */ static int commit_ref_update(struct ref_lock *lock, const unsigned char *sha1, const char *logmsg, - int flags, struct strbuf *err) + struct strbuf *err) { clear_loose_ref_cache(&ref_cache); - if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0 || + if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err) < 0 || (strcmp(lock->ref_name, lock->orig_ref_name) && - log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0)) { + log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg, 0, err) < 0)) { char *old_msg = strbuf_detach(err, NULL); strbuf_addf(err, "cannot update the ref '%s': %s", lock->ref_name, old_msg); @@ -2921,7 +2921,7 @@ static int commit_ref_update(struct ref_lock *lock, } } } - if (!(flags & REF_LOG_ONLY) && commit_ref(lock)) { + if (commit_ref(lock)) { strbuf_addf(err, "couldn't set '%s'", lock->ref_name); unlock_ref(lock); return -1; -- cgit v1.2.1 From 7a418f3a17b95746eb94cfd55f4fe0385d058777 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Fri, 22 Apr 2016 15:25:25 +0200 Subject: lock_ref_sha1_basic(): only handle REF_NODEREF mode Now lock_ref_sha1_basic() is only called with flags==REF_NODEREF. So we don't have to handle other cases anymore. This enables several simplifications, the most interesting of which come from the fact that ref_lock::orig_ref_name is now always the same as ref_lock::ref_name: * Remove ref_lock::orig_ref_name * Remove local variable orig_refname from lock_ref_sha1_basic() * ref_name can be initialize once and its value reused * commit_ref_update() never has to write to the reflog for lock->orig_ref_name Signed-off-by: Michael Haggerty --- refs/files-backend.c | 54 +++++++++++++++++++--------------------------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/refs/files-backend.c b/refs/files-backend.c index 7f9949352b..bbf96ad83a 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -7,7 +7,6 @@ struct ref_lock { char *ref_name; - char *orig_ref_name; struct lock_file *lk; struct object_id old_oid; }; @@ -1522,7 +1521,6 @@ static void unlock_ref(struct ref_lock *lock) if (lock->lk) rollback_lock_file(lock->lk); free(lock->ref_name); - free(lock->orig_ref_name); free(lock); } @@ -1576,7 +1574,6 @@ static int lock_raw_ref(const char *refname, int mustexist, *lock_p = lock = xcalloc(1, sizeof(*lock)); lock->ref_name = xstrdup(refname); - lock->orig_ref_name = xstrdup(refname); strbuf_git_path(&ref_file, "%s", refname); retry: @@ -1969,14 +1966,13 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, struct strbuf *err) { struct strbuf ref_file = STRBUF_INIT; - struct strbuf orig_ref_file = STRBUF_INIT; - const char *orig_refname = refname; struct ref_lock *lock; int last_errno = 0; - int lflags = 0; + int lflags = LOCK_NO_DEREF; int mustexist = (old_sha1 && !is_null_sha1(old_sha1)); - int resolve_flags = 0; + int resolve_flags = RESOLVE_REF_NO_RECURSE; int attempts_remaining = 3; + int resolved; assert(err); @@ -1986,46 +1982,39 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, resolve_flags |= RESOLVE_REF_READING; if (flags & REF_DELETING) resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME; - if (flags & REF_NODEREF) { - resolve_flags |= RESOLVE_REF_NO_RECURSE; - lflags |= LOCK_NO_DEREF; - } - refname = resolve_ref_unsafe(refname, resolve_flags, - lock->old_oid.hash, type); - if (!refname && errno == EISDIR) { + strbuf_git_path(&ref_file, "%s", refname); + resolved = !!resolve_ref_unsafe(refname, resolve_flags, + lock->old_oid.hash, type); + if (!resolved && errno == EISDIR) { /* * we are trying to lock foo but we used to * have foo/bar which now does not exist; * it is normal for the empty directory 'foo' * to remain. */ - strbuf_git_path(&orig_ref_file, "%s", orig_refname); - if (remove_empty_directories(&orig_ref_file)) { + if (remove_empty_directories(&ref_file)) { last_errno = errno; - if (!verify_refname_available_dir(orig_refname, extras, skip, + if (!verify_refname_available_dir(refname, extras, skip, get_loose_refs(&ref_cache), err)) strbuf_addf(err, "there are still refs under '%s'", - orig_refname); + refname); goto error_return; } - refname = resolve_ref_unsafe(orig_refname, resolve_flags, - lock->old_oid.hash, type); + resolved = !!resolve_ref_unsafe(refname, resolve_flags, + lock->old_oid.hash, type); } - if (!refname) { + if (!resolved) { last_errno = errno; if (last_errno != ENOTDIR || - !verify_refname_available_dir(orig_refname, extras, skip, + !verify_refname_available_dir(refname, extras, skip, get_loose_refs(&ref_cache), err)) strbuf_addf(err, "unable to resolve reference '%s': %s", - orig_refname, strerror(last_errno)); + refname, strerror(last_errno)); goto error_return; } - if (flags & REF_NODEREF) - refname = orig_refname; - /* * If the ref did not exist and we are creating it, make sure * there is no existing packed ref whose name begins with our @@ -2042,8 +2031,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, lock->lk = xcalloc(1, sizeof(struct lock_file)); lock->ref_name = xstrdup(refname); - lock->orig_ref_name = xstrdup(orig_refname); - strbuf_git_path(&ref_file, "%s", refname); retry: switch (safe_create_leading_directories_const(ref_file.buf)) { @@ -2086,7 +2073,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, out: strbuf_release(&ref_file); - strbuf_release(&orig_ref_file); errno = last_errno; return lock; } @@ -2883,9 +2869,7 @@ static int commit_ref_update(struct ref_lock *lock, struct strbuf *err) { clear_loose_ref_cache(&ref_cache); - if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err) < 0 || - (strcmp(lock->ref_name, lock->orig_ref_name) && - log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg, 0, err) < 0)) { + if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err)) { char *old_msg = strbuf_detach(err, NULL); strbuf_addf(err, "cannot update the ref '%s': %s", lock->ref_name, old_msg); @@ -2893,7 +2877,8 @@ static int commit_ref_update(struct ref_lock *lock, unlock_ref(lock); return -1; } - if (strcmp(lock->orig_ref_name, "HEAD") != 0) { + + if (strcmp(lock->ref_name, "HEAD") != 0) { /* * Special hack: If a branch is updated directly and HEAD * points to it (may happen on the remote side of a push @@ -2909,6 +2894,7 @@ static int commit_ref_update(struct ref_lock *lock, unsigned char head_sha1[20]; int head_flag; const char *head_ref; + head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, &head_flag); if (head_ref && (head_flag & REF_ISSYMREF) && @@ -2921,6 +2907,7 @@ static int commit_ref_update(struct ref_lock *lock, } } } + if (commit_ref(lock)) { strbuf_addf(err, "couldn't set '%s'", lock->ref_name); unlock_ref(lock); @@ -3026,7 +3013,6 @@ int set_worktree_head_symref(const char *gitdir, const char *target) lock = xcalloc(1, sizeof(struct ref_lock)); lock->lk = &head_lock; lock->ref_name = xstrdup(head_rel); - lock->orig_ref_name = xstrdup(head_rel); ret = create_symref_locked(lock, head_rel, target, NULL); -- cgit v1.2.1 From 1354c9b2ded11a2bc24e04b98268a5969b44c666 Mon Sep 17 00:00:00 2001 From: Michael Haggerty Date: Thu, 31 Mar 2016 06:19:22 +0200 Subject: refs: remove unnecessary "extern" keywords There's continuing work in this area, so clean up unneeded "extern" keywords rather than schlepping them around. Also split up some overlong lines and add parameter names in a couple of places. Signed-off-by: Michael Haggerty --- refs.h | 134 +++++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 73 insertions(+), 61 deletions(-) diff --git a/refs.h b/refs.h index 9230d47142..21874f023b 100644 --- a/refs.h +++ b/refs.h @@ -52,19 +52,19 @@ #define RESOLVE_REF_NO_RECURSE 0x02 #define RESOLVE_REF_ALLOW_BAD_NAME 0x04 -extern const char *resolve_ref_unsafe(const char *refname, int resolve_flags, - unsigned char *sha1, int *flags); +const char *resolve_ref_unsafe(const char *refname, int resolve_flags, + unsigned char *sha1, int *flags); -extern char *resolve_refdup(const char *refname, int resolve_flags, - unsigned char *sha1, int *flags); +char *resolve_refdup(const char *refname, int resolve_flags, + unsigned char *sha1, int *flags); -extern int read_ref_full(const char *refname, int resolve_flags, - unsigned char *sha1, int *flags); -extern int read_ref(const char *refname, unsigned char *sha1); +int read_ref_full(const char *refname, int resolve_flags, + unsigned char *sha1, int *flags); +int read_ref(const char *refname, unsigned char *sha1); -extern int ref_exists(const char *refname); +int ref_exists(const char *refname); -extern int is_branch(const char *refname); +int is_branch(const char *refname); /* * If refname is a non-symbolic reference that refers to a tag object, @@ -74,24 +74,25 @@ extern int is_branch(const char *refname); * Symbolic references are considered unpeelable, even if they * ultimately resolve to a peelable tag. */ -extern int peel_ref(const char *refname, unsigned char *sha1); +int peel_ref(const char *refname, unsigned char *sha1); /** * Resolve refname in the nested "gitlink" repository that is located * at path. If the resolution is successful, return 0 and set sha1 to * the name of the object; otherwise, return a non-zero value. */ -extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1); +int resolve_gitlink_ref(const char *path, const char *refname, + unsigned char *sha1); /* * Return true iff abbrev_name is a possible abbreviation for * full_name according to the rules defined by ref_rev_parse_rules in * refs.c. */ -extern int refname_match(const char *abbrev_name, const char *full_name); +int refname_match(const char *abbrev_name, const char *full_name); -extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); -extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); +int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); +int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); /* * A ref_transaction represents a collection of ref updates @@ -182,38 +183,45 @@ typedef int each_ref_fn(const char *refname, * modifies the reference also returns a nonzero value to immediately * stop the iteration. */ -extern int head_ref(each_ref_fn fn, void *cb_data); -extern int for_each_ref(each_ref_fn fn, void *cb_data); -extern int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data); -extern int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken); -extern int for_each_tag_ref(each_ref_fn fn, void *cb_data); -extern int for_each_branch_ref(each_ref_fn fn, void *cb_data); -extern int for_each_remote_ref(each_ref_fn fn, void *cb_data); -extern int for_each_replace_ref(each_ref_fn fn, void *cb_data); -extern int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data); -extern int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const char *prefix, void *cb_data); - -extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); -extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); -extern int for_each_ref_in_submodule(const char *submodule, const char *prefix, +int head_ref(each_ref_fn fn, void *cb_data); +int for_each_ref(each_ref_fn fn, void *cb_data); +int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data); +int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, + unsigned int broken); +int for_each_tag_ref(each_ref_fn fn, void *cb_data); +int for_each_branch_ref(each_ref_fn fn, void *cb_data); +int for_each_remote_ref(each_ref_fn fn, void *cb_data); +int for_each_replace_ref(each_ref_fn fn, void *cb_data); +int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data); +int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, + const char *prefix, void *cb_data); + +int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); +int for_each_ref_submodule(const char *submodule, + each_ref_fn fn, void *cb_data); +int for_each_ref_in_submodule(const char *submodule, const char *prefix, each_ref_fn fn, void *cb_data); -extern int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); -extern int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); -extern int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data); +int for_each_tag_ref_submodule(const char *submodule, + each_ref_fn fn, void *cb_data); +int for_each_branch_ref_submodule(const char *submodule, + each_ref_fn fn, void *cb_data); +int for_each_remote_ref_submodule(const char *submodule, + each_ref_fn fn, void *cb_data); -extern int head_ref_namespaced(each_ref_fn fn, void *cb_data); -extern int for_each_namespaced_ref(each_ref_fn fn, void *cb_data); +int head_ref_namespaced(each_ref_fn fn, void *cb_data); +int for_each_namespaced_ref(each_ref_fn fn, void *cb_data); /* can be used to learn about broken ref and symref */ -extern int for_each_rawref(each_ref_fn fn, void *cb_data); +int for_each_rawref(each_ref_fn fn, void *cb_data); static inline const char *has_glob_specials(const char *pattern) { return strpbrk(pattern, "?*["); } -extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname); -extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames); +void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname); +void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, + const struct string_list *refnames); /* * Flags for controlling behaviour of pack_refs() @@ -245,13 +253,13 @@ int pack_refs(unsigned int flags); int safe_create_reflog(const char *refname, int force_create, struct strbuf *err); /** Reads log for the value of ref during at_time. **/ -extern int read_ref_at(const char *refname, unsigned int flags, - unsigned long at_time, int cnt, - unsigned char *sha1, char **msg, - unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt); +int read_ref_at(const char *refname, unsigned int flags, + unsigned long at_time, int cnt, + unsigned char *sha1, char **msg, + unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt); /** Check if a particular reflog exists */ -extern int reflog_exists(const char *refname); +int reflog_exists(const char *refname); /* * Delete the specified reference. If old_sha1 is non-NULL, then @@ -260,21 +268,25 @@ extern int reflog_exists(const char *refname); * exists, regardless of its old value. It is an error for old_sha1 to * be NULL_SHA1. flags is passed through to ref_transaction_delete(). */ -extern int delete_ref(const char *refname, const unsigned char *old_sha1, - unsigned int flags); +int delete_ref(const char *refname, const unsigned char *old_sha1, + unsigned int flags); /* * Delete the specified references. If there are any problems, emit * errors but attempt to keep going (i.e., the deletes are not done in * an all-or-nothing transaction). */ -extern int delete_refs(struct string_list *refnames); +int delete_refs(struct string_list *refnames); /** Delete a reflog */ -extern int delete_reflog(const char *refname); +int delete_reflog(const char *refname); /* iterate over reflog entries */ -typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *); +typedef int each_reflog_ent_fn( + unsigned char *old_sha1, unsigned char *new_sha1, + const char *committer, unsigned long timestamp, + int tz, const char *msg, void *cb_data); + int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data); int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data); @@ -282,7 +294,7 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void * Calls the specified function for each reflog file until it returns nonzero, * and returns the value */ -extern int for_each_reflog(each_ref_fn, void *); +int for_each_reflog(each_ref_fn fn, void *cb_data); #define REFNAME_ALLOW_ONELEVEL 1 #define REFNAME_REFSPEC_PATTERN 2 @@ -295,16 +307,16 @@ extern int for_each_reflog(each_ref_fn, void *); * allow a single "*" wildcard character in the refspec. No leading or * repeated slashes are accepted. */ -extern int check_refname_format(const char *refname, int flags); +int check_refname_format(const char *refname, int flags); -extern const char *prettify_refname(const char *refname); +const char *prettify_refname(const char *refname); -extern char *shorten_unambiguous_ref(const char *refname, int strict); +char *shorten_unambiguous_ref(const char *refname, int strict); /** rename ref, return 0 on success **/ -extern int rename_ref(const char *oldref, const char *newref, const char *logmsg); +int rename_ref(const char *oldref, const char *newref, const char *logmsg); -extern int create_symref(const char *refname, const char *target, const char *logmsg); +int create_symref(const char *refname, const char *target, const char *logmsg); /* * Update HEAD of the specified gitdir. @@ -313,7 +325,7 @@ extern int create_symref(const char *refname, const char *target, const char *lo * $GIT_DIR points to. * Return 0 if successful, non-zero otherwise. * */ -extern int set_worktree_head_symref(const char *gitdir, const char *target); +int set_worktree_head_symref(const char *gitdir, const char *target); enum action_on_err { UPDATE_REFS_MSG_ON_ERR, @@ -463,7 +475,7 @@ int update_ref(const char *msg, const char *refname, const unsigned char *new_sha1, const unsigned char *old_sha1, unsigned int flags, enum action_on_err onerr); -extern int parse_hide_refs_config(const char *var, const char *value, const char *); +int parse_hide_refs_config(const char *var, const char *value, const char *); /* * Check whether a ref is hidden. If no namespace is set, both the first and @@ -473,7 +485,7 @@ extern int parse_hide_refs_config(const char *var, const char *value, const char * the ref is outside that namespace, the first parameter is NULL. The second * parameter always points to the full ref name. */ -extern int ref_is_hidden(const char *, const char *); +int ref_is_hidden(const char *, const char *); enum ref_type { REF_TYPE_PER_WORKTREE, @@ -522,11 +534,11 @@ typedef void reflog_expiry_cleanup_fn(void *cb_data); * enum expire_reflog_flags. The three function pointers are described * above. On success, return zero. */ -extern int reflog_expire(const char *refname, const unsigned char *sha1, - unsigned int flags, - reflog_expiry_prepare_fn prepare_fn, - reflog_expiry_should_prune_fn should_prune_fn, - reflog_expiry_cleanup_fn cleanup_fn, - void *policy_cb_data); +int reflog_expire(const char *refname, const unsigned char *sha1, + unsigned int flags, + reflog_expiry_prepare_fn prepare_fn, + reflog_expiry_should_prune_fn should_prune_fn, + reflog_expiry_cleanup_fn cleanup_fn, + void *policy_cb_data); #endif /* REFS_H */ -- cgit v1.2.1 From 665b35eccd39fefd714cb5c332277a6b94fd9386 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Thu, 9 Jun 2016 17:35:36 -0700 Subject: submodule--helper: initial clone learns retry logic Each submodule that is attempted to be cloned, will be retried once in case of failure after all other submodules were cloned. This helps to mitigate ephemeral server failures and increases chances of a reliable clone of a repo with hundreds of submodules immensely. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 66 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index ca33408f55..8d01fdd1f0 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -592,10 +592,14 @@ struct submodule_update_clone { /* If we want to stop as fast as possible and return an error */ unsigned quickstop : 1; + + /* failed clones to be retried again */ + const struct cache_entry **failed_clones; + int failed_clones_nr, failed_clones_alloc; }; #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \ SUBMODULE_UPDATE_STRATEGY_INIT, 0, -1, NULL, NULL, NULL, NULL, \ - STRING_LIST_INIT_DUP, 0} + STRING_LIST_INIT_DUP, 0, NULL, 0, 0} static void next_submodule_warn_missing(struct submodule_update_clone *suc, @@ -720,23 +724,47 @@ cleanup: static int update_clone_get_next_task(struct child_process *child, struct strbuf *err, void *suc_cb, - void **void_task_cb) + void **idx_task_cb) { struct submodule_update_clone *suc = suc_cb; + const struct cache_entry *ce; + int index; for (; suc->current < suc->list.nr; suc->current++) { - const struct cache_entry *ce = suc->list.entries[suc->current]; + ce = suc->list.entries[suc->current]; if (prepare_to_clone_next_submodule(ce, child, suc, err)) { + int *p = xmalloc(sizeof(*p)); + *p = suc->current; + *idx_task_cb = p; suc->current++; return 1; } } + + /* + * The loop above tried cloning each submodule once, now try the + * stragglers again, which we can imagine as an extension of the + * entry list. + */ + index = suc->current - suc->list.nr; + if (index < suc->failed_clones_nr) { + int *p; + ce = suc->failed_clones[index]; + if (!prepare_to_clone_next_submodule(ce, child, suc, err)) + die("BUG: ce was a submodule before?"); + p = xmalloc(sizeof(*p)); + *p = suc->current; + *idx_task_cb = p; + suc->current ++; + return 1; + } + return 0; } static int update_clone_start_failure(struct strbuf *err, void *suc_cb, - void *void_task_cb) + void *idx_task_cb) { struct submodule_update_clone *suc = suc_cb; suc->quickstop = 1; @@ -746,15 +774,39 @@ static int update_clone_start_failure(struct strbuf *err, static int update_clone_task_finished(int result, struct strbuf *err, void *suc_cb, - void *void_task_cb) + void *idx_task_cb) { + const struct cache_entry *ce; struct submodule_update_clone *suc = suc_cb; + int *idxP = *(int**)idx_task_cb; + int idx = *idxP; + free(idxP); + if (!result) return 0; - suc->quickstop = 1; - return 1; + if (idx < suc->list.nr) { + ce = suc->list.entries[idx]; + strbuf_addf(err, _("Failed to clone '%s'. Retry scheduled"), + ce->name); + strbuf_addch(err, '\n'); + ALLOC_GROW(suc->failed_clones, + suc->failed_clones_nr + 1, + suc->failed_clones_alloc); + suc->failed_clones[suc->failed_clones_nr++] = ce; + return 0; + } else { + idx = suc->current - suc->list.nr; + ce = suc->failed_clones[idx]; + strbuf_addf(err, _("Failed to clone '%s' a second time, aborting"), + ce->name); + strbuf_addch(err, '\n'); + suc->quickstop = 1; + return 1; + } + + return 0; } static int update_clone(int argc, const char **argv, const char *prefix) -- cgit v1.2.1 From bb9d91b4ed54df7bd970c82971ba2851e6735d72 Mon Sep 17 00:00:00 2001 From: Stefan Beller Date: Thu, 9 Jun 2016 12:06:37 -0700 Subject: submodule update: continue when a clone fails In 15ffb7cde48 (2011-06-13, submodule update: continue when a checkout fails), we reasoned it is ok to continue, when there is not much of a mental burden by the failure. If a recursive submodule fails to clone because a .gitmodules file is broken (e.g. : fatal: No url found for submodule path 'foo/bar' in .gitmodules Failed to recurse into submodule path 'foo', signaled by exit code 128), this is one of the cases where the user is not expected to have much of a burden afterwards, so we can also continue in that case. This means we only want to stop for updating submodules in case of rebase, merge or custom update command failures, which are all signaled with exit code 2. Signed-off-by: Stefan Beller Signed-off-by: Junio C Hamano --- git-submodule.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-submodule.sh b/git-submodule.sh index 42e0e9f63d..f1919ca16f 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -734,7 +734,7 @@ cmd_update() if test $res -gt 0 then die_msg="$(eval_gettext "Failed to recurse into submodule path '\$displaypath'")" - if test $res -eq 1 + if test $res -ne 2 then err="${err};$die_msg" continue -- cgit v1.2.1 From 7a7a517a2f6264a893ed47f8daf02cb221aca67c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 13 Jun 2016 01:39:12 -0400 Subject: parse_opt_string_list: stop allocating new strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The parse_opt_string_list callback is basically a thin wrapper to string_list_append() any string options we get. However, it calls: string_list_append(v, xstrdup(arg)); which duplicates the option value. This is wrong for two reasons: 1. If the string list has strdup_strings set, then we are making an extra copy, which is simply leaked. 2. If the string list does not have strdup_strings set, then we pass memory ownership to the string list, but it does not realize this. If we later call string_list_clear(), which can happen if "--no-foo" is passed, then we will leak all of the existing entries. Instead, we should just pass the argument straight to string_list_append, and it can decide whether to copy or not based on its strdup_strings flag. It's possible that some (buggy) caller could be relying on this extra copy (e.g., because it parses some options from an allocated argv array and then frees the array), but it's not likely. For one, we generally only use parse_options on the argv given to us in main(). And two, such a caller is broken anyway, because other option types like OPT_STRING() do not make such a copy. This patch brings us in line with them. Noticed-by: Nguyễn Thái Ngọc Duy Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- parse-options-cb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse-options-cb.c b/parse-options-cb.c index 5ab6ed6b08..4e8290181f 100644 --- a/parse-options-cb.c +++ b/parse-options-cb.c @@ -127,7 +127,7 @@ int parse_opt_string_list(const struct option *opt, const char *arg, int unset) if (!arg) return -1; - string_list_append(v, xstrdup(arg)); + string_list_append(v, arg); return 0; } -- cgit v1.2.1 From 7c4b169585ebe650783051c4b7a7b17de62836ad Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 13 Jun 2016 01:39:20 -0400 Subject: interpret-trailers: don't duplicate option strings There's no need to do so; the argv strings will last until the end of the program. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/interpret-trailers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c index 46838d24a9..b75e953111 100644 --- a/builtin/interpret-trailers.c +++ b/builtin/interpret-trailers.c @@ -19,7 +19,7 @@ static const char * const git_interpret_trailers_usage[] = { int cmd_interpret_trailers(int argc, const char **argv, const char *prefix) { int trim_empty = 0; - struct string_list trailers = STRING_LIST_INIT_DUP; + struct string_list trailers = STRING_LIST_INIT_NODUP; struct option options[] = { OPT_BOOL(0, "trim-empty", &trim_empty, N_("trim empty trailers")), -- cgit v1.2.1 From 64093fc06a871f71316211a2aea6bb46c49b20ab Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 13 Jun 2016 01:39:28 -0400 Subject: blame,shortlog: don't make local option variables static There's no need for these option variables to be static, except that they are referenced by the options array itself, which is static. But having all of this static is simply unnecessary and confusing (and inconsistent with most other commands, which either use a static global option list or a true function-local one). Note that in some cases we may need to actually initialize the variables (since we cannot rely on BSS to do so). This is a net improvement to readability, though, as we can use the more verbose initializers for our string_lists. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/blame.c | 12 ++++++------ builtin/shortlog.c | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 6cac59c973..9b1701d314 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -2503,12 +2503,12 @@ int cmd_blame(int argc, const char **argv, const char *prefix) char *final_commit_name = NULL; enum object_type type; - static struct string_list range_list; - static int output_option = 0, opt = 0; - static int show_stats = 0; - static const char *revs_file = NULL; - static const char *contents_from = NULL; - static const struct option options[] = { + struct string_list range_list = STRING_LIST_INIT_NODUP; + int output_option = 0, opt = 0; + int show_stats = 0; + const char *revs_file = NULL; + const char *contents_from = NULL; + const struct option options[] = { OPT_BOOL(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")), OPT_BOOL('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")), OPT_BOOL(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")), diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 007cc66a03..cb3e89cf1d 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -221,11 +221,11 @@ void shortlog_init(struct shortlog *log) int cmd_shortlog(int argc, const char **argv, const char *prefix) { - static struct shortlog log; - static struct rev_info rev; + struct shortlog log = { STRING_LIST_INIT_NODUP }; + struct rev_info rev; int nongit = !startup_info->have_repository; - static const struct option options[] = { + const struct option options[] = { OPT_BOOL('n', "numbered", &log.sort_by_number, N_("sort output according to the number of commits per author")), OPT_BOOL('s', "summary", &log.summary, -- cgit v1.2.1 From 2721ce21e439ee0726dc69073acd7e0d2b2407b3 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 13 Jun 2016 06:04:20 -0400 Subject: use string_list initializer consistently There are two types of string_lists: those that own the string memory, and those that don't. You can tell the difference by the strdup_strings flag, and one should use either STRING_LIST_INIT_DUP, or STRING_LIST_INIT_NODUP as an initializer. Historically, the normal all-zeros initialization has corresponded to the NODUP case. Many sites use no initializer at all, and that works as a shorthand for that case. But for a reader of the code, it can be hard to remember which is which. Let's be more explicit and actually have each site declare which type it means to use. This is a fairly mechanical conversion; I assumed each site was correct as-is, and just switched them all to NODUP. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/apply.c | 6 +++--- builtin/blame.c | 2 +- builtin/clone.c | 4 ++-- builtin/log.c | 6 +++--- builtin/remote.c | 2 +- notes.c | 2 +- submodule.c | 2 +- t/helper/test-parse-options.c | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/builtin/apply.c b/builtin/apply.c index 8e4da2e1bd..955af4db6c 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -270,7 +270,7 @@ struct image { * the case where more than one patches touch the same file. */ -static struct string_list fn_table; +static struct string_list fn_table = STRING_LIST_INIT_NODUP; static uint32_t hash_line(const char *cp, size_t len) { @@ -1936,7 +1936,7 @@ static void prefix_patch(struct patch *p) * include/exclude */ -static struct string_list limit_by_name; +static struct string_list limit_by_name = STRING_LIST_INIT_NODUP; static int has_include; static void add_name_limit(const char *name, int exclude) { @@ -3582,7 +3582,7 @@ static int check_to_create(const char *new_name, int ok_if_exists) * it is perfectly fine, as the patch removes a/b to make room * to create a directory a/b so that a/b/c can be created. */ -static struct string_list symlink_changes; +static struct string_list symlink_changes = STRING_LIST_INIT_NODUP; #define SYMLINK_GOES_AWAY 01 #define SYMLINK_IN_RESULT 02 diff --git a/builtin/blame.c b/builtin/blame.c index 80d24315b3..de70153685 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -56,7 +56,7 @@ static int show_progress; static struct date_mode blame_date_mode = { DATE_ISO8601 }; static size_t blame_date_width; -static struct string_list mailmap; +static struct string_list mailmap = STRING_LIST_INIT_NODUP; #ifndef DEBUG #define DEBUG 0 diff --git a/builtin/clone.c b/builtin/clone.c index 5f867e67d8..70d8213472 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -49,8 +49,8 @@ static char *option_upload_pack = "git-upload-pack"; static int option_verbosity; static int option_progress = -1; static enum transport_family family; -static struct string_list option_config; -static struct string_list option_reference; +static struct string_list option_config = STRING_LIST_INIT_NODUP; +static struct string_list option_reference = STRING_LIST_INIT_NODUP; static int option_dissociate; static int max_jobs = -1; diff --git a/builtin/log.c b/builtin/log.c index dff3fbbb43..c11bd66f39 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -674,9 +674,9 @@ static int auto_number = 1; static char *default_attach = NULL; -static struct string_list extra_hdr; -static struct string_list extra_to; -static struct string_list extra_cc; +static struct string_list extra_hdr = STRING_LIST_INIT_NODUP; +static struct string_list extra_to = STRING_LIST_INIT_NODUP; +static struct string_list extra_cc = STRING_LIST_INIT_NODUP; static void add_header(const char *value) { diff --git a/builtin/remote.c b/builtin/remote.c index fda5c2e53d..e1cc55e13c 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -247,7 +247,7 @@ struct branch_info { enum { NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE } rebase; }; -static struct string_list branch_list; +static struct string_list branch_list = STRING_LIST_INIT_NODUP; static const char *abbrev_ref(const char *name, const char *prefix) { diff --git a/notes.c b/notes.c index e4e4854d69..df4660fe62 100644 --- a/notes.c +++ b/notes.c @@ -70,7 +70,7 @@ struct non_note { struct notes_tree default_notes_tree; -static struct string_list display_notes_refs; +static struct string_list display_notes_refs = STRING_LIST_INIT_NODUP; static struct notes_tree **display_notes_trees; static void load_subtree(struct notes_tree *t, struct leaf_node *subtree, diff --git a/submodule.c b/submodule.c index 4532b11d66..abc2ac2a10 100644 --- a/submodule.c +++ b/submodule.c @@ -17,7 +17,7 @@ static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND; static int parallel_jobs = 1; -static struct string_list changed_submodule_paths; +static struct string_list changed_submodule_paths = STRING_LIST_INIT_NODUP; static int initialized_fetch_ref_tips; static struct sha1_array ref_tips_before_fetch; static struct sha1_array ref_tips_after_fetch; diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index 2c8c8f18ed..37a196781d 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -11,7 +11,7 @@ static int verbose = 0, dry_run = 0, quiet = 0; static char *string = NULL; static char *file = NULL; static int ambiguous; -static struct string_list list; +static struct string_list list = STRING_LIST_INIT_NODUP; static int length_callback(const struct option *opt, const char *arg, int unset) { -- cgit v1.2.1 From 44f243d356a6402878b2b3e098fd8cfa4fe1aaf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 13 Jun 2016 19:35:09 +0700 Subject: lib-httpd.sh: print error.log on error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Failure to bring up httpd for testing is not considered an error, so the trash directory, which contains this error.log file, is removed and we don't know what made httpd fail to start. Improve the situation a bit, print error.log but only in verbose mode. Signed-off-by: Nguyễn Thái Ngọc Duy Reviewed-by: Jeff King Signed-off-by: Junio C Hamano --- t/lib-httpd.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index f9f3e5fd82..ac2cbee250 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -180,6 +180,7 @@ start_httpd() { if test $? -ne 0 then trap 'die' EXIT + cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null test_skip_or_die $GIT_TEST_HTTPD "web server setup failed" fi } -- cgit v1.2.1 From 346ef53058ef25f5a7273ee77c03ebc5f732ad77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 13 Jun 2016 19:18:23 +0700 Subject: worktree.c: add is_worktree_locked() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need this later to avoid double locking a worktree, or unlocking one when it's not even locked. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- worktree.c | 28 ++++++++++++++++++++++++++++ worktree.h | 8 ++++++++ 2 files changed, 36 insertions(+) diff --git a/worktree.c b/worktree.c index 12a766a38d..2bcfff3850 100644 --- a/worktree.c +++ b/worktree.c @@ -13,6 +13,7 @@ void free_worktrees(struct worktree **worktrees) free(worktrees[i]->path); free(worktrees[i]->id); free(worktrees[i]->head_ref); + free(worktrees[i]->lock_reason); free(worktrees[i]); } free (worktrees); @@ -98,6 +99,8 @@ static struct worktree *get_main_worktree(void) worktree->is_detached = is_detached; worktree->is_current = 0; add_head_info(&head_ref, worktree); + worktree->lock_reason = NULL; + worktree->lock_reason_valid = 0; done: strbuf_release(&path); @@ -143,6 +146,8 @@ static struct worktree *get_linked_worktree(const char *id) worktree->is_detached = is_detached; worktree->is_current = 0; add_head_info(&head_ref, worktree); + worktree->lock_reason = NULL; + worktree->lock_reason_valid = 0; done: strbuf_release(&path); @@ -234,6 +239,29 @@ int is_main_worktree(const struct worktree *wt) return !wt->id; } +const char *is_worktree_locked(struct worktree *wt) +{ + assert(!is_main_worktree(wt)); + + if (!wt->lock_reason_valid) { + struct strbuf path = STRBUF_INIT; + + strbuf_addstr(&path, worktree_git_path(wt, "locked")); + if (file_exists(path.buf)) { + struct strbuf lock_reason = STRBUF_INIT; + if (strbuf_read_file(&lock_reason, path.buf, 0) < 0) + die_errno(_("failed to read '%s'"), path.buf); + strbuf_trim(&lock_reason); + wt->lock_reason = strbuf_detach(&lock_reason, NULL); + } else + wt->lock_reason = NULL; + wt->lock_reason_valid = 1; + strbuf_release(&path); + } + + return wt->lock_reason; +} + int is_worktree_being_rebased(const struct worktree *wt, const char *target) { diff --git a/worktree.h b/worktree.h index e1c4715238..90e1311fa7 100644 --- a/worktree.h +++ b/worktree.h @@ -5,10 +5,12 @@ struct worktree { char *path; char *id; char *head_ref; + char *lock_reason; /* internal use */ unsigned char head_sha1[20]; int is_detached; int is_bare; int is_current; + int lock_reason_valid; }; /* Functions for acting on the information about worktrees. */ @@ -42,6 +44,12 @@ extern struct worktree *find_worktree(struct worktree **list, */ extern int is_main_worktree(const struct worktree *wt); +/* + * Return the reason string if the given worktree is locked or NULL + * otherwise. + */ +extern const char *is_worktree_locked(struct worktree *wt); + /* * Free up the memory for worktree(s) */ -- cgit v1.2.1 From 6a7bcb54714c55234a292047ea6bb657bd5ab1b5 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 13 Jun 2016 00:33:54 -0400 Subject: repack: document --unpack-unreachable option This was added back in 7e52f56 (gc: do not explode objects which will be immediately pruned, 2012-04-07), but not documented at the time, since it was an internal detail between git-gc and git-repack. However, as people with complicated setups may want to effectively reimplement the steps of git-gc themselves, it is nice for us to document these interfaces. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-repack.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index b9c02ce481..cde7b4441d 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -128,6 +128,12 @@ other objects in that pack they already have locally. with `-b` or `repack.writeBitmaps`, as it ensures that the bitmapped packfile has the necessary objects. +--unpack-unreachable=:: + When loosening unreachable objects, do not bother loosening any + objects older than ``. This can be used to optimize out + the write of any objects that would be immediately pruned by + a follow-up `git prune`. + Configuration ------------- -- cgit v1.2.1 From 905f27b86ac1f50c6870a064c3b5b9d82c97a145 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 13 Jun 2016 00:36:28 -0400 Subject: repack: add --keep-unreachable option The usual way to do a full repack (and what is done by git-gc) is to run "repack -Ad --unpack-unreachable=", which will loosen any unreachable objects newer than "", and drop any older ones. This is a safer alternative to "repack -ad", because "" becomes a grace period during which we will not drop any new objects that are about to be referenced. However, it isn't perfectly safe. It's always possible that a process is about to reference an old object. Even if that process were to take care to update the timestamp on the object, there is no atomicity with a simultaneously running "repack" process. So while unlikely, there is a small race wherein we may drop an object that is in the process of being referenced. If you do automated repacking on a large number of active repositories, you may hit it eventually, and the result is a corrupted repository. It would be nice to fix that race in the long run, but it's complicated. In the meantime, there is a much simpler strategy for automated repository maintenance: do not drop objects at all. We already have a "--keep-unreachable" option in pack-objects; we just need to plumb it through from git-repack. Note that this _isn't_ plumbed through from git-gc, so at this point it's strictly a tool for people doing their own advanced repository maintenance strategy. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-repack.txt | 6 ++++++ builtin/repack.c | 9 +++++++++ t/t7701-repack-unpack-unreachable.sh | 15 +++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index cde7b4441d..68702eab19 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -134,6 +134,12 @@ other objects in that pack they already have locally. the write of any objects that would be immediately pruned by a follow-up `git prune`. +-k:: +--keep-unreachable:: + When used with `-ad`, any unreachable objects from existing + packs will be appended to the end of the packfile instead of + being removed. + Configuration ------------- diff --git a/builtin/repack.c b/builtin/repack.c index 858db38f52..573e66c1b4 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -146,6 +146,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) int pack_everything = 0; int delete_redundant = 0; const char *unpack_unreachable = NULL; + int keep_unreachable = 0; const char *window = NULL, *window_memory = NULL; const char *depth = NULL; const char *max_pack_size = NULL; @@ -175,6 +176,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("write bitmap index")), OPT_STRING(0, "unpack-unreachable", &unpack_unreachable, N_("approxidate"), N_("with -A, do not loosen objects older than this")), + OPT_BOOL('k', "keep-unreachable", &keep_unreachable, + N_("with -a, repack unreachable objects")), OPT_STRING(0, "window", &window, N_("n"), N_("size of the window used for delta compression")), OPT_STRING(0, "window-memory", &window_memory, N_("bytes"), @@ -196,6 +199,10 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (delete_redundant && repository_format_precious_objects) die(_("cannot delete packs in a precious-objects repo")); + if (keep_unreachable && + (unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE))) + die(_("--keep-unreachable and -A are incompatible")); + if (pack_kept_objects < 0) pack_kept_objects = write_bitmaps; @@ -239,6 +246,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) } else if (pack_everything & LOOSEN_UNREACHABLE) { argv_array_push(&cmd.args, "--unpack-unreachable"); + } else if (keep_unreachable) { + argv_array_push(&cmd.args, "--keep-unreachable"); } else { argv_array_push(&cmd.env_array, "GIT_REF_PARANOIA=1"); } diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh index b66e383866..f13df43299 100755 --- a/t/t7701-repack-unpack-unreachable.sh +++ b/t/t7701-repack-unpack-unreachable.sh @@ -122,4 +122,19 @@ test_expect_success 'keep packed objects found only in index' ' git cat-file blob :file ' +test_expect_success 'repack -k keeps unreachable packed objects' ' + # create packed-but-unreachable object + sha1=$(echo unreachable-packed | git hash-object -w --stdin) && + pack=$(echo $sha1 | git pack-objects .git/objects/pack/pack) && + git prune-packed && + + # -k should keep it + git repack -adk && + git cat-file -p $sha1 && + + # and double check that without -k it would have been removed + git repack -ad && + test_must_fail git cat-file -p $sha1 +' + test_done -- cgit v1.2.1 From e26a8c4721ceaf4c59e33bbd4e60f777b7ea9b62 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 13 Jun 2016 00:38:04 -0400 Subject: repack: extend --keep-unreachable to loose objects If you use "repack -adk" currently, we will pack all objects that are already packed into the new pack, and then drop the old packs. However, loose unreachable objects will be left as-is. In theory these are meant to expire eventually with "git prune". But if you are using "repack -k", you probably want to keep things forever and therefore do not run "git prune" at all. Meaning those loose objects may build up over time and end up fooling any object-count heuristics (such as the one done by "gc --auto", though since git-gc does not support "repack -k", this really applies to whatever custom scripts people might have driving "repack -k"). With this patch, we instead stuff any loose unreachable objects into the pack along with the already-packed unreachable objects. This may seem wasteful, but it is really no more so than using "repack -k" in the first place. We are at a slight disadvantage, in that we have no useful ordering for the result, or names to hand to the delta code. However, this is again no worse than what "repack -k" is already doing for the packed objects. The packing of these objects doesn't matter much because they should not be accessed frequently (unless they actually _do_ become referenced, but then they would get moved to a different part of the packfile during the next repack). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-repack.txt | 3 ++- builtin/pack-objects.c | 31 +++++++++++++++++++++++++++++++ builtin/repack.c | 1 + t/t7701-repack-unpack-unreachable.sh | 13 +++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index 68702eab19..b58b6b5972 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -138,7 +138,8 @@ other objects in that pack they already have locally. --keep-unreachable:: When used with `-ad`, any unreachable objects from existing packs will be appended to the end of the packfile instead of - being removed. + being removed. In addition, any unreachable loose objects will + be packed (and their loose counterparts removed). Configuration ------------- diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 8f5e358e22..a2f8cfdec0 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -44,6 +44,7 @@ static int non_empty; static int reuse_delta = 1, reuse_object = 1; static int keep_unreachable, unpack_unreachable, include_tag; static unsigned long unpack_unreachable_expiration; +static int pack_loose_unreachable; static int local; static int incremental; static int ignore_packed_keep; @@ -2378,6 +2379,32 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs) free(in_pack.array); } +static int add_loose_object(const unsigned char *sha1, const char *path, + void *data) +{ + enum object_type type = sha1_object_info(sha1, NULL); + + if (type < 0) { + warning("loose object at %s could not be examined", path); + return 0; + } + + add_object_entry(sha1, type, "", 0); + return 0; +} + +/* + * We actually don't even have to worry about reachability here. + * add_object_entry will weed out duplicates, so we just add every + * loose object we find. + */ +static void add_unreachable_loose_objects(void) +{ + for_each_loose_file_in_objdir(get_object_directory(), + add_loose_object, + NULL, NULL, NULL); +} + static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1) { static struct packed_git *last_found = (void *)1; @@ -2547,6 +2574,8 @@ static void get_object_list(int ac, const char **av) if (keep_unreachable) add_objects_in_unpacked_packs(&revs); + if (pack_loose_unreachable) + add_unreachable_loose_objects(); if (unpack_unreachable) loosen_unused_packed_objects(&revs); @@ -2647,6 +2676,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) N_("include tag objects that refer to objects to be packed")), OPT_BOOL(0, "keep-unreachable", &keep_unreachable, N_("keep unreachable objects")), + OPT_BOOL(0, "pack-loose-unreachable", &pack_loose_unreachable, + N_("pack loose unreachable objects")), { OPTION_CALLBACK, 0, "unpack-unreachable", NULL, N_("time"), N_("unpack unreachable objects newer than