Code clean-up in refs API implementation.
* mh/tidy-ref-update-flags:
refs: update some more docs to use "oid" rather than "sha1"
write_packed_entry(): take `object_id` arguments
refs: rename constant `REF_ISPRUNING` to `REF_IS_PRUNING`
refs: rename constant `REF_NODEREF` to `REF_NO_DEREF`
refs: tidy up and adjust visibility of the `ref_update` flags
ref_transaction_add_update(): remove a check
ref_transaction_update(): die on disallowed flags
prune_ref(): call `ref_transaction_add_update()` directly
files_transaction_prepare(): don't leak flags to packed transaction
Junio C Hamano <gitster@pobox.com> <junio@twinsun.com>
Junio C Hamano <gitster@pobox.com> <junkio@cox.net>
Junio C Hamano <gitster@pobox.com> <junkio@twinsun.com>
+Kaartic Sivaraam <kaartic.sivaraam@gmail.com> <kaarticsivaraam91196@gmail.com>
Karl Wiberg <kha@treskal.com> Karl Hasselström
Karl Wiberg <kha@treskal.com> <kha@yoghurt.hemma.treskal.com>
Karsten Blees <blees@dcon.de> <karsten.blees@dcon.de>
packages:
- coccinelle
before_install:
- # "before_script" that builds Git is inherited from base job
+ before_script:
script: ci/run-static-analysis.sh
after_failure:
- env: Documentation
learned to take the 'unfold' and 'only' modifiers to normalize its
output, e.g. "git log --format=%(trailers:only,unfold)".
- * "gitweb" shows a link to visit the 'raw' contents of blbos in the
+ * "gitweb" shows a link to visit the 'raw' contents of blobs in the
history overview page.
* "[gc] rerereResolved = 5.days" used to be invalid, as the variable
* Conversion from uchar[20] to struct object_id continues.
* Start using selected c99 constructs in small, stable and
- essentialpart of the system to catch people who care about
+ essential part of the system to catch people who care about
older compilers that do not grok them.
* The filter-process interface learned to allow a process with long
latency give a "delayed" response.
- * Many uses of comparision callback function the hashmap API uses
+ * Many uses of comparison callback function the hashmap API uses
cast the callback function type when registering it to
hashmap_init(), which defeats the compile time type checking when
the callback interface changes (e.g. gaining more parameters).
--- /dev/null
+Git 2.16 Release Notes
+======================
+
+Backward compatibility notes and other notable changes.
+
+ * Use of an empty string as a pathspec element that is used for
+ 'everything matches' is now an error.
+
+
+Updates since v2.15
+-------------------
+
+UI, Workflows & Features
+
+ * An empty string as a pathspec element that means "everything"
+ i.e. 'git add ""', is now illegal. We started this by first
+ deprecating and warning a pathspec that has such an element in
+ 2.11 (Nov 2016).
+
+ * A hook script that is set unexecutable is simply ignored. Git
+ notifies when such a file is ignored, unless the message is
+ squelched via advice.ignoredHook configuration.
+
+ * "git pull" has been taught to accept "--[no-]signoff" option and
+ pass it down to "git merge".
+
+ * The "--push-option=<string>" option to "git push" now defaults to a
+ list of strings configured via push.pushOption variable.
+
+ * "gitweb" checks if a directory is searchable with Perl's "-x"
+ operator, which can be enhanced by using "filetest 'access'"
+ pragma, which now we do.
+
+ * "git stash save" has been deprecated in favour of "git stash push".
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * An earlier update made it possible to use an on-stack in-core
+ lockfile structure (as opposed to having to deliberately leak an
+ on-heap one). Many codepaths have been updated to take advantage
+ of this new facility.
+
+ * Calling cmd_foo() as if it is a general purpose helper function is
+ a no-no. Correct two instances of such to set an example.
+
+ * We try to see if somebody runs our test suite with a shell that
+ does not support "local" like bash/dash does.
+
+ * An early part of piece-by-piece rewrite of "git bisect" in C.
+
+ * GSoC to piece-by-piece rewrite "git submodule" in C.
+
+ * Optimize the code to find shortest unique prefix of object names.
+
+ * Pathspec-limited revision traversal was taught not to keep finding
+ unneeded differences once it knows two trees are different inside
+ given pathspec.
+
+ * Conversion from uchar[20] to struct object_id continues.
+
+ * Code cleanup.
+ (merge 62a24c8923 rs/hex-to-bytes-cleanup later to maint).
+
+ * A single-word "unsigned flags" in the diff options is being split
+ into a structure with many bitfields.
+ (merge 0d1e0e7801 bw/diff-opt-impl-to-bitfields later to maint).
+
+ * TravisCI build updates.
+ (merge c2154953b8 sg/travis-fixes later to maint).
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.15
+-----------------
+
+ * "auto" as a value for the columnar output configuration ought to
+ judge "is the output consumed by humans?" with the same criteria as
+ "auto" for coloured output configuration, i.e. either the standard
+ output stream is going to tty, or a pager is in use. We forgot the
+ latter, which has been fixed.
+ (merge 965ff23a43 kd/auto-col-with-pager-fix later to maint).
+
+ * The experimental "color moved lines differently in diff output"
+ feature was buggy around "ignore whitespace changes" edges, whihch
+ has been corrected.
+ (merge b66b507292 jk/diff-color-moved-fix later to maint).
+
+ * Instead of using custom line comparison and hashing functions to
+ implement "moved lines" coloring in the diff output, use the pair
+ of these functions from lower-layer xdiff/ code.
+ (merge 01be97c2b2 sb/diff-color-moved-use-xdl-recmatch later to maint).
+
+ * Some codepaths did not check for errors when asking what branch the
+ HEAD points at, which have been fixed.
+ (merge dbd2b55cb7 jk/misc-resolve-ref-unsafe-fixes later to maint).
+
+ * "git commit", after making a commit, did not check for errors when
+ asking on what branch it made the commit, which has been correted.
+ (merge c26de08370 ao/check-resolve-ref-unsafe-result later to maint).
+
+ * "git status --ignored -u" did not stop at a working tree of a
+ separate project that is embedded in an ignored directory and
+ listed files in that other project, instead of just showing the
+ directory itself as ignored.
+ (merge fadb4820c4 js/submodule-in-excluded later to maint).
+
+ * A broken access to object databases in recent update to "git grep
+ --recurse-submodules" has been fixed.
+ (merge 9560e6245a bw/grep-recurse-submodules later to maint).
+
+ * A recent regression in "git rebase -i" that broke execution of git
+ commands from subdirectories via "exec" insn has been fixed.
+ (merge 09d7b6c6fa jk/rebase-i-exec-gitdir-fix later to maint).
+
+ * A (possibly flakey) test fix.
+ (merge cff48ccf2a jc/t5601-copy-workaround later to maint).
+
+ * "git check-ref-format --branch @{-1}" bit a "BUG()" when run
+ outside a repository for obvious reasons; clarify the documentation
+ and make sure we do not even try to expand the at-mark magic in
+ such a case, but still call the validation logic for branch names.
+ (merge 89dd32aedc jc/check-ref-format-oor later to maint).
+
+ * "git fetch --recurse-submodules" now knows that submodules can be
+ moved around in the superproject in addition to getting updated,
+ and finds the ones that need to be fetched accordingly.
+ (merge 4b4acedd61 hv/fetch-moved-submodules-on-demand later to maint).
+
+ * Command line completion (in contrib/) update.
+ (merge 6357d9d004 tb/complete-checkout later to maint).
+
+ * Description of blame.{showroot,blankboundary,showemail,date}
+ configuration variables have been added to "git config --help".
+ (merge de0bc11d13 sb/blame-config-doc later to maint).
+
+ * After an error from lstat(), diff_populate_filespec() function
+ sometimes still went ahead and used invalid data in struct stat,
+ which has been fixed.
+ (merge 10e0ca843d ao/diff-populate-filespec-lstat-errorpath-fix later to maint).
+
+ * UNC paths are also relevant in Cygwin builds and they are now
+ tested just like Mingw builds.
+ (merge f21d60b429 ad/5580-unc-tests-on-cygwin later to maint).
+
+ * Correct start-up sequence so that a repository could be placed
+ immediately under the root directory again (which was broken at
+ around Git 2.13).
+ (merge fa4d8c783d js/early-config later to maint).
+
+ * The credential helper for libsecret (in contrib/) has been improved
+ to allow possibly prompting the end user to unlock secrets that are
+ currently locked (otherwise the secrets may not be loaded).
+ (merge 9c109e9bbc dk/libsecret-unlock-to-load-fix later to maint).
+
+ * MinGW updates.
+ (merge 39bb86b4e5 js/mingw-full-version-in-resources later to maint).
+ (merge 601e1e7897 js/wincred-empty-cred later to maint).
+ (merge b2f55717c7 js/mingw-redirect-std-handles later to maint).
+
+ * Other minor doc, test and build updates and code cleanups.
+ (merge bab76141da cn/diff-indent-no-longer-is-experimental later to maint).
+ (merge 8684dde10d jm/relnotes-2.15-typofix later to maint).
+ (merge cd3f8e2fc2 ks/mailmap later to maint).
addEmbeddedRepo::
Advice on what to do when you've accidentally added one
git repo inside of another.
+ ignoredHook::
+ Advice shown if an hook is ignored because the hook is not
+ set as executable.
--
core.fileMode::
Tells 'git apply' how to handle whitespaces, in the same way
as the `--whitespace` option. See linkgit:git-apply[1].
+blame.showRoot::
+ Do not treat root commits as boundaries in linkgit:git-blame[1].
+ This option defaults to false.
+
+blame.blankBoundary::
+ Show blank commit object name for boundary commits in
+ linkgit:git-blame[1]. This option defaults to false.
+
+blame.showEmail::
+ Show the author email instead of author name in linkgit:git-blame[1].
+ This option defaults to false.
+
+blame.date::
+ Specifies the format used to output dates in linkgit:git-blame[1].
+ If unset the iso format is used. For supported values,
+ see the discussion of the `--date` option at linkgit:git-log[1].
+
branch.autoSetupMerge::
Tells 'git branch' and 'git checkout' to set up new branches
so that linkgit:git-pull[1] will appropriately merge from the
override a value from a lower-priority config file. An explicit
command-line flag always overrides this config option.
+push.pushOption::
+ When no `--push-option=<option>` argument is given from the
+ command line, `git push` behaves as if each <value> of
+ this variable is given as `--push-option=<value>`.
++
+This is a multi-valued variable, and an empty value can be used in a
+higher priority configuration file (e.g. `.git/config` in a
+repository) to clear the values inherited from a lower priority
+configuration files (e.g. `$HOME/.gitconfig`).
++
+--
+
+Example:
+
+/etc/gitconfig
+ push.pushoption = a
+ push.pushoption = b
+
+~/.gitconfig
+ push.pushoption = c
+
+repo/.git/config
+ push.pushoption =
+ push.pushoption = b
+
+This will result in only b (a and c are cleared).
+
+--
+
push.recurseSubmodules::
Make sure all submodule commits used by the revisions to be pushed
are available on a remote-tracking branch. If the value is 'check'
+++ /dev/null
---indent-heuristic::
---no-indent-heuristic::
- These are to help debugging and tuning experimental heuristics
- (which are off by default) that shift diff hunk boundaries to
- make patches easier to read.
Synonym for `-p --raw`.
endif::git-format-patch[]
-include::diff-heuristic-options.txt[]
+--indent-heuristic::
+ Enable the heuristic that shift diff hunk boundaries to make patches
+ easier to read. This is the default.
+
+--no-indent-heuristic::
+ Disable the indent heuristic.
--minimal::
Spend extra time to make sure the smallest possible
OPTIONS
-------
include::blame-options.txt[]
-include::diff-heuristic-options.txt[]
SEE ALSO
--------
abbreviated object name, use <n>+1 digits. Note that 1 column
is used for a caret to mark the boundary commit.
-include::diff-heuristic-options.txt[]
-
THE PORCELAIN FORMAT
--------------------
. at-open-brace `@{` is used as a notation to access a reflog entry.
-With the `--branch` option, it expands the ``previous branch syntax''
+With the `--branch` option, the command takes a name and checks if
+it can be used as a valid branch name (e.g. when creating a new
+branch). The rule `git check-ref-format --branch $name` implements
+may be stricter than what `git check-ref-format refs/heads/$name`
+says (e.g. a dash may appear at the beginning of a ref component,
+but it is explicitly forbidden at the beginning of a branch name).
+When run with `--branch` option in a repository, the input is first
+expanded for the ``previous branch syntax''
`@{-n}`. For example, `@{-1}` is a way to refer the last branch you
were on. This option should be used by porcelains to accept this
syntax anywhere a branch name is expected, so they can act as if you
-------
include::merge-options.txt[]
---signoff::
- Add Signed-off-by line by the committer at the end of the commit
- log message. The meaning of a signoff depends on the project,
- but it typically certifies that committer has
- the rights to submit this work under the same license and
- agrees to a Developer Certificate of Origin
- (see http://developercertificate.org/ for more information).
-
-m <msg>::
Set the commit message to be used for the merge commit (in
case one is created).
Either all refs are updated, or on error, no refs are updated.
If the server does not support atomic pushes the push will fail.
--o::
---push-option::
+-o <option>::
+--push-option=<option>::
Transmit the given string to the server, which passes them to
the pre-receive as well as the post-receive hook. The given string
must not contain a NUL or LF character.
+ When multiple `--push-option=<option>` are given, they are
+ all sent to the other side in the order listed on the
+ command line.
+ When no `--push-option=<option>` is given from the command
+ line, the values of configuration variable `push.pushOption`
+ are used instead.
--receive-pack=<git-receive-pack>::
--exec=<git-receive-pack>::
'git stash' drop [-q|--quiet] [<stash>]
'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
'git stash' branch <branchname> [<stash>]
-'git stash' save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
- [-u|--include-untracked] [-a|--all] [<message>]
'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
[-u|--include-untracked] [-a|--all] [-m|--message <message>]]
[--] [<pathspec>...]]
The modifications stashed away by this command can be listed with
`git stash list`, inspected with `git stash show`, and restored
(potentially on top of a different commit) with `git stash apply`.
-Calling `git stash` without any arguments is equivalent to `git stash save`.
+Calling `git stash` without any arguments is equivalent to `git stash push`.
A stash is by default listed as "WIP on 'branchname' ...", but
you can give a more descriptive message on the command line when
you create one.
OPTIONS
-------
-save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
Save your local modifications to a new 'stash entry' and roll them
The `--patch` option implies `--keep-index`. You can use
`--no-keep-index` to override this.
+save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
+
+ This option is deprecated in favour of 'git stash push'. It
+ differs from "stash push" in that it cannot take pathspecs,
+ and any non-option arguments form the message.
+
list [<options>]::
List the stash entries that you currently have. Each 'stash entry' is
Remove a single stashed state from the stash list and apply it
on top of the current working tree state, i.e., do the inverse
- operation of `git stash save`. The working directory must
+ operation of `git stash push`. The working directory must
match the index.
+
Applying the state can fail with conflicts; in this case, it is not
Like `pop`, but do not remove the state from the stash list. Unlike `pop`,
`<stash>` may be any commit that looks like a commit created by
- `stash save` or `stash create`.
+ `stash push` or `stash create`.
branch <branchname> [<stash>]::
`stash@{<revision>}`, it then drops the `<stash>`. When no `<stash>`
is given, applies the latest one.
+
-This is useful if the branch on which you ran `git stash save` has
+This is useful if the branch on which you ran `git stash push` has
changed enough that `git stash apply` fails due to conflicts. Since
the stash entry is applied on top of the commit that was HEAD at the
time `git stash` was run, it restores the originally stashed state
Testing partial commits::
-You can use `git stash save --keep-index` when you want to make two or
+You can use `git stash push --keep-index` when you want to make two or
more commits out of the changes in the work tree, and you want to test
each change before committing:
+
----------------------------------------------------------------
# ... hack hack hack ...
$ git add --patch foo # add just first part to the index
-$ git stash save --keep-index # save all other changes to the stash
+$ git stash push --keep-index # save all other changes to the stash
$ edit/build/test first part
$ git commit -m 'First part' # commit fully tested change
$ git stash pop # prepare to work on all other changes
(and suppresses the output of submodule summaries when the config option
`status.submoduleSummary` is set).
---ignored::
+--ignored[=<mode>]::
Show ignored files as well.
++
+The mode parameter is used to specify the handling of ignored files.
+It is optional: it defaults to 'traditional'.
++
+The possible options are:
++
+ - 'traditional' - Shows ignored files and directories, unless
+ --untracked-files=all is specifed, in which case
+ individual files in ignored directories are
+ displayed.
+ - 'no' - Show no ignored files.
+ - 'matching' - Shows ignored files and directories matching an
+ ignore pattern.
++
+When 'matching' mode is specified, paths that explicity match an
+ignored pattern are shown. If a directory matches an ignore pattern,
+then it is shown, but not paths contained in the ignored directory. If
+a directory does not match an ignore pattern, but all contents are
+ignored, then the directory is not shown, but all contents are shown.
-z::
Terminate entries with NUL, instead of LF. This implies
the background which do not want to cause lock contention with
other operations on the repository. Defaults to `1`.
+`GIT_REDIRECT_STDIN`::
+`GIT_REDIRECT_STDOUT`::
+`GIT_REDIRECT_STDERR`::
+ Windows-only: allow redirecting the standard input/output/error
+ handles to paths specified by the environment variables. This is
+ particularly useful in multi-threaded applications where the
+ canonical way to pass standard handles via `CreateProcess()` is
+ not an option because it would require the handles to be marked
+ inheritable (and consequently *every* spawned process would
+ inherit them, possibly blocking regular Git operations). The
+ primary intended use case is to use named pipes for communication
+ (e.g. `\\.\pipe\my-git-stdin-123`).
++
+Two special values are supported: `off` will simply close the
+corresponding standard handle, and if `GIT_REDIRECT_STDERR` is
+`2>&1`, standard error will be redirected to the same handle as
+standard output.
+
Discussion[[Discussion]]
------------------------
to split one big commit into several. Don't be afraid of making too
small or imperfect steps along the way. You can always go back later
and edit the commits with `git rebase --interactive` before you
-publish them. You can use `git stash save --keep-index` to run the
+publish them. You can use `git stash push --keep-index` to run the
test suite independent of other uncommitted changes; see the EXAMPLES
section of linkgit:git-stash[1].
With --no-log do not list one-line descriptions from the
actual commits being merged.
+--signoff::
+--no-signoff::
+ Add Signed-off-by line by the committer at the end of the commit
+ log message. The meaning of a signoff depends on the project,
+ but it typically certifies that committer has
+ the rights to submit this work under the same license and
+ agrees to a Developer Certificate of Origin
+ (see http://developercertificate.org/ for more information).
++
+With --no-signoff do not add a Signed-off-by line.
--stat::
-n::
`flags`::
- A bit-field of options (the `*IGNORED*` flags are mutually exclusive):
+ A bit-field of options:
`DIR_SHOW_IGNORED`:::
- Return just ignored files in `entries[]`, not untracked files.
+ Return just ignored files in `entries[]`, not untracked
+ files. This flag is mutually exclusive with
+ `DIR_SHOW_IGNORED_TOO`.
`DIR_SHOW_IGNORED_TOO`:::
- Similar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`
- in addition to untracked files in `entries[]`.
+ Similar to `DIR_SHOW_IGNORED`, but return ignored files in
+ `ignored[]` in addition to untracked files in
+ `entries[]`. This flag is mutually exclusive with
+ `DIR_SHOW_IGNORED`.
`DIR_KEEP_UNTRACKED_CONTENTS`:::
untracked contents of untracked directories are also returned in
`entries[]`.
+`DIR_SHOW_IGNORED_TOO_MODE_MATCHING`:::
+
+ Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if
+ this is set, returns ignored files and directories that match
+ an exclude pattern. If a directory matches an exclude pattern,
+ then the directory is returned and the contained paths are
+ not. A directory that does not match an exclude pattern will
+ not be returned even if all of its contents are ignored. In
+ this case, the contents are returned as individual entries.
++
+If this is set, files and directories that explicity match an ignore
+pattern are reported. Implicity ignored directories (directories that
+do not match an ignore pattern, but whose contents are all ignored)
+are not reported, instead all of the contents are reported.
+
`DIR_COLLECT_IGNORED`:::
Special mode for git-add. Return ignored files in `ignored[]` and
work-in-progress changes.
------------------------------------------------
-$ git stash save "work in progress for foo feature"
+$ git stash push -m "work in progress for foo feature"
------------------------------------------------
This command will save your changes away to the `stash`, and
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.15.0-rc2
+DEF_VER=v2.15.GIT
LF='
'
git.res: git.rc GIT-VERSION-FILE
$(QUIET_RC)$(RC) \
- $(join -DMAJOR= -DMINOR=, $(wordlist 1,2,$(subst -, ,$(subst ., ,$(GIT_VERSION))))) \
+ $(join -DMAJOR= -DMINOR= -DMICRO= -DPATCHLEVEL=, $(wordlist 1, 4, \
+ $(shell echo $(GIT_VERSION) 0 0 0 0 | tr '.a-zA-Z-' ' '))) \
-DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" -i $< -o $@
# This makes sure we depend on the NO_PERL setting itself.
-Documentation/RelNotes/2.15.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.16.0.txt
\ No newline at end of file
int advice_object_name_warning = 1;
int advice_rm_hints = 1;
int advice_add_embedded_repo = 1;
+int advice_ignored_hook = 1;
static struct {
const char *name;
{ "objectnamewarning", &advice_object_name_warning },
{ "rmhints", &advice_rm_hints },
{ "addembeddedrepo", &advice_add_embedded_repo },
+ { "ignoredhook", &advice_ignored_hook },
/* make this an alias for backward compatibility */
{ "pushnonfastforward", &advice_push_update_rejected }
extern int advice_object_name_warning;
extern int advice_rm_hints;
extern int advice_add_embedded_repo;
+extern int advice_ignored_hook;
int git_default_advice_config(const char *var, const char *value);
__attribute__((format (printf, 1, 2)))
}
int init_apply_state(struct apply_state *state,
- const char *prefix,
- struct lock_file *lock_file)
+ const char *prefix)
{
memset(state, 0, sizeof(*state));
state->prefix = prefix;
- state->lock_file = lock_file;
- state->newfd = -1;
state->apply = 1;
state->line_termination = '\n';
state->p_value = 1;
}
if (state->check_index)
state->unsafe_paths = 0;
- if (!state->lock_file)
- return error("BUG: state->lock_file should not be NULL");
if (state->apply_verbosity <= verbosity_silent) {
state->saved_error_routine = get_error_routine();
state->apply = 0;
state->update_index = state->check_index && state->apply;
- if (state->update_index && state->newfd < 0) {
+ if (state->update_index && !is_lock_file_locked(&state->lock_file)) {
if (state->index_file)
- state->newfd = hold_lock_file_for_update(state->lock_file,
- state->index_file,
- LOCK_DIE_ON_ERROR);
+ hold_lock_file_for_update(&state->lock_file,
+ state->index_file,
+ LOCK_DIE_ON_ERROR);
else
- state->newfd = hold_locked_index(state->lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&state->lock_file, LOCK_DIE_ON_ERROR);
}
if (state->check_index && read_apply_cache(state) < 0) {
}
if (state->update_index) {
- res = write_locked_index(&the_index, state->lock_file, COMMIT_LOCK);
+ res = write_locked_index(&the_index, &state->lock_file, COMMIT_LOCK);
if (res) {
error(_("Unable to write new index file"));
res = -128;
goto end;
}
- state->newfd = -1;
}
res = !!errs;
end:
- if (state->newfd >= 0) {
- rollback_lock_file(state->lock_file);
- state->newfd = -1;
- }
+ rollback_lock_file(&state->lock_file);
if (state->apply_verbosity <= verbosity_silent) {
set_error_routine(state->saved_error_routine);
struct apply_state {
const char *prefix;
- /* These are lock_file related */
- struct lock_file *lock_file;
- int newfd;
+ /* Lock file */
+ struct lock_file lock_file;
/* These control what gets looked at and modified */
int apply; /* this is not a dry-run */
int *force_apply, int *options,
const char * const *apply_usage);
extern int init_apply_state(struct apply_state *state,
- const char *prefix,
- struct lock_file *lock_file);
+ const char *prefix);
extern void clear_apply_state(struct apply_state *state);
extern int check_apply_state(struct apply_state *state, int force_apply);
add_name_decoration(DECORATION_NONE, buf.buf, obj);
p->item = array[i].commit;
- p = p->next;
+ if (i < cnt - 1)
+ p = p->next;
}
- if (p)
- p->next = NULL;
+ free_commit_list(p->next);
+ p->next = NULL;
strbuf_release(&buf);
free(array);
return list;
return best_bisection_sorted(list, nr);
}
-struct commit_list *find_bisection(struct commit_list *list,
- int *reaches, int *all,
- int find_all)
+void find_bisection(struct commit_list **commit_list, int *reaches,
+ int *all, int find_all)
{
int nr, on_list;
- struct commit_list *p, *best, *next, *last;
+ struct commit_list *list, *p, *best, *next, *last;
int *weights;
- show_list("bisection 2 entry", 0, 0, list);
+ show_list("bisection 2 entry", 0, 0, *commit_list);
/*
* Count the number of total and tree-changing items on the
* list, while reversing the list.
*/
- for (nr = on_list = 0, last = NULL, p = list;
+ for (nr = on_list = 0, last = NULL, p = *commit_list;
p;
p = next) {
unsigned flags = p->item->object.flags;
next = p->next;
- if (flags & UNINTERESTING)
+ if (flags & UNINTERESTING) {
+ free(p);
continue;
+ }
p->next = last;
last = p;
if (!(flags & TREESAME))
/* Do the real work of finding bisection commit. */
best = do_find_bisection(list, nr, weights, find_all);
if (best) {
- if (!find_all)
+ if (!find_all) {
+ list->item = best->item;
+ free_commit_list(list->next);
+ best = list;
best->next = NULL;
+ }
*reaches = weight(best);
}
free(weights);
- return best;
+ *commit_list = best;
}
static int register_ref(const char *refname, const struct object_id *oid,
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
+static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
+static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
+static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
+static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
+static GIT_PATH_FUNC(git_path_head_name, "head-name")
static void read_bisect_paths(struct argv_array *array)
{
bisect_common(&revs);
- revs.commits = find_bisection(revs.commits, &reaches, &all,
- !!skipped_revs.nr);
+ find_bisection(&revs.commits, &reaches, &all, !!skipped_revs.nr);
revs.commits = managed_skipped(revs.commits, &tried);
if (!revs.commits) {
return (e < 3 * x) ? n : n - 1;
}
+
+static int mark_for_removal(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
+{
+ struct string_list *refs = cb_data;
+ char *ref = xstrfmt("refs/bisect%s", refname);
+ string_list_append(refs, ref);
+ return 0;
+}
+
+int bisect_clean_state(void)
+{
+ int result = 0;
+
+ /* There may be some refs packed during bisection */
+ struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
+ for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
+ string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
+ result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF);
+ refs_for_removal.strdup_strings = 1;
+ string_list_clear(&refs_for_removal, 0);
+ unlink_or_warn(git_path_bisect_expected_rev());
+ unlink_or_warn(git_path_bisect_ancestors_ok());
+ unlink_or_warn(git_path_bisect_log());
+ unlink_or_warn(git_path_bisect_names());
+ unlink_or_warn(git_path_bisect_run());
+ unlink_or_warn(git_path_bisect_terms());
+ /* Cleanup head-name if it got left by an old version of git-bisect */
+ unlink_or_warn(git_path_head_name());
+ /*
+ * Cleanup BISECT_START last to support the --no-checkout option
+ * introduced in the commit 4796e823a.
+ */
+ unlink_or_warn(git_path_bisect_start());
+
+ return result;
+}
#ifndef BISECT_H
#define BISECT_H
-extern struct commit_list *find_bisection(struct commit_list *list,
- int *reaches, int *all,
- int find_all);
+/*
+ * Find bisection. If something is found, `reaches` will be the number of
+ * commits that the best commit reaches. `all` will be the count of
+ * non-SAMETREE commits. If nothing is found, `list` will be NULL.
+ * Otherwise, it will be either all non-SAMETREE commits or the single
+ * best commit, as chosen by `find_all`.
+ */
+extern void find_bisection(struct commit_list **list, int *reaches, int *all,
+ int find_all);
extern struct commit_list *filter_skipped(struct commit_list *list,
struct commit_list **tried,
extern void read_bisect_terms(const char **bad, const char **good);
+extern int bisect_clean_state(void);
+
#endif
switch (st.st_mode & S_IFMT) {
case S_IFREG:
- if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+ if (opt->flags.allow_textconv &&
textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
unsigned long file_size;
(*num_read_blob)++;
- if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+ if (opt->flags.allow_textconv &&
textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))
;
else
* same and diff-tree is fairly efficient about this.
*/
diff_setup(&diff_opts);
- DIFF_OPT_SET(&diff_opts, RECURSIVE);
+ diff_opts.flags.recursive = 1;
diff_opts.detect_rename = 0;
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
paths[0] = origin->path;
int i;
diff_setup(&diff_opts);
- DIFF_OPT_SET(&diff_opts, RECURSIVE);
+ diff_opts.flags.recursive = 1;
diff_opts.detect_rename = DIFF_DETECT_RENAME;
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
diff_opts.single_follow = origin->path;
return; /* nothing remains for this target */
diff_setup(&diff_opts);
- DIFF_OPT_SET(&diff_opts, RECURSIVE);
+ diff_opts.flags.recursive = 1;
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
diff_setup_done(&diff_opts);
if ((opt & PICKAXE_BLAME_COPY_HARDEST)
|| ((opt & PICKAXE_BLAME_COPY_HARDER)
&& (!porigin || strcmp(target->path, porigin->path))))
- DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
+ diff_opts.flags.find_copies_harder = 1;
if (is_null_oid(&target->commit->object.oid))
do_diff_cache(&parent->tree->object.oid, &diff_opts);
&target->commit->tree->object.oid,
"", &diff_opts);
- if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
+ if (!diff_opts.flags.find_copies_harder)
diffcore_std(&diff_opts);
do {
if (fill_blob_sha1_and_mode(o))
die(_("no such path %s in %s"), path, final_commit_name);
- if (DIFF_OPT_TST(&sb->revs->diffopt, ALLOW_TEXTCONV) &&
+ if (sb->revs->diffopt.flags.allow_textconv &&
textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,
&sb->final_buf_size))
;
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = update_callback;
rev.diffopt.format_callback_data = &data;
- rev.diffopt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+ rev.diffopt.flags.override_submodule_config = 1;
rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
clear_pathspec(&rev.prune_data);
argc = setup_revisions(argc, argv, &rev, NULL);
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
rev.diffopt.use_color = 0;
- DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
+ rev.diffopt.flags.ignore_dirty_submodules = 1;
out = open(file, O_CREAT | O_WRONLY, 0666);
if (out < 0)
die(_("Could not open '%s' for writing."), file);
*/
static void refresh_and_write_cache(void)
{
- struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+ struct lock_file lock_file = LOCK_INIT;
- hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
refresh_cache(REFRESH_QUIET);
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write index file"));
}
struct diff_options opt;
diff_setup(&opt);
- DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);
+ opt.flags.exit_with_status = 1;
if (!sb)
- DIFF_OPT_SET(&opt, QUICK);
+ opt.flags.quick = 1;
do_diff_cache(&head, &opt);
diffcore_std(&opt);
for (i = 0; sb && i < diff_queued_diff.nr; i++) {
strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);
}
diff_flush(&opt);
- return DIFF_OPT_TST(&opt, HAS_CHANGES) != 0;
+ return opt.flags.has_changes != 0;
} else {
for (i = 0; sb && i < active_nr; i++) {
if (i)
rev_info.show_root_diff = 1;
rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;
rev_info.no_commit_id = 1;
- DIFF_OPT_SET(&rev_info.diffopt, BINARY);
- DIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);
+ rev_info.diffopt.flags.binary = 1;
+ rev_info.diffopt.flags.full_index = 1;
rev_info.diffopt.use_color = 0;
rev_info.diffopt.file = fp;
rev_info.diffopt.close_file = 1;
struct argv_array apply_opts = ARGV_ARRAY_INIT;
struct apply_state apply_state;
int res, opts_left;
- static struct lock_file lock_file;
int force_apply = 0;
int options = 0;
- if (init_apply_state(&apply_state, NULL, &lock_file))
+ if (init_apply_state(&apply_state, NULL))
die("BUG: init_apply_state() failed");
argv_array_push(&apply_opts, "apply");
*/
static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
{
- struct lock_file *lock_file;
+ struct lock_file lock_file = LOCK_INIT;
struct unpack_trees_options opts;
struct tree_desc t[2];
if (parse_tree(head) || parse_tree(remote))
return -1;
- lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
refresh_cache(REFRESH_QUIET);
init_tree_desc(&t[1], remote->buffer, remote->size);
if (unpack_trees(2, t, &opts)) {
- rollback_lock_file(lock_file);
+ rollback_lock_file(&lock_file);
return -1;
}
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
return 0;
*/
static int merge_tree(struct tree *tree)
{
- struct lock_file *lock_file;
+ struct lock_file lock_file = LOCK_INIT;
struct unpack_trees_options opts;
struct tree_desc t[1];
if (parse_tree(tree))
return -1;
- lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
init_tree_desc(&t[0], tree->buffer, tree->size);
if (unpack_trees(1, t, &opts)) {
- rollback_lock_file(lock_file);
+ rollback_lock_file(&lock_file);
return -1;
}
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
return 0;
NULL
};
-static struct lock_file lock_file;
-
int cmd_apply(int argc, const char **argv, const char *prefix)
{
int force_apply = 0;
int ret;
struct apply_state state;
- if (init_apply_state(&state, prefix, &lock_file))
+ if (init_apply_state(&state, prefix))
exit(128);
argc = apply_parse_options(argc, argv,
#include "cache.h"
#include "parse-options.h"
#include "bisect.h"
+#include "refs.h"
+
+static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
+static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
+static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
static const char * const git_bisect_helper_usage[] = {
N_("git bisect--helper --next-all [--no-checkout]"),
+ N_("git bisect--helper --write-terms <bad_term> <good_term>"),
+ N_("git bisect--helper --bisect-clean-state"),
NULL
};
+/*
+ * Check whether the string `term` belongs to the set of strings
+ * included in the variable arguments.
+ */
+LAST_ARG_MUST_BE_NULL
+static int one_of(const char *term, ...)
+{
+ int res = 0;
+ va_list matches;
+ const char *match;
+
+ va_start(matches, term);
+ while (!res && (match = va_arg(matches, const char *)))
+ res = !strcmp(term, match);
+ va_end(matches);
+
+ return res;
+}
+
+static int check_term_format(const char *term, const char *orig_term)
+{
+ int res;
+ char *new_term = xstrfmt("refs/bisect/%s", term);
+
+ res = check_refname_format(new_term, 0);
+ free(new_term);
+
+ if (res)
+ return error(_("'%s' is not a valid term"), term);
+
+ if (one_of(term, "help", "start", "skip", "next", "reset",
+ "visualize", "replay", "log", "run", "terms", NULL))
+ return error(_("can't use the builtin command '%s' as a term"), term);
+
+ /*
+ * In theory, nothing prevents swapping completely good and bad,
+ * but this situation could be confusing and hasn't been tested
+ * enough. Forbid it for now.
+ */
+
+ if ((strcmp(orig_term, "bad") && one_of(term, "bad", "new", NULL)) ||
+ (strcmp(orig_term, "good") && one_of(term, "good", "old", NULL)))
+ return error(_("can't change the meaning of the term '%s'"), term);
+
+ return 0;
+}
+
+static int write_terms(const char *bad, const char *good)
+{
+ FILE *fp = NULL;
+ int res;
+
+ if (!strcmp(bad, good))
+ return error(_("please use two different terms"));
+
+ if (check_term_format(bad, "bad") || check_term_format(good, "good"))
+ return -1;
+
+ fp = fopen(git_path_bisect_terms(), "w");
+ if (!fp)
+ return error_errno(_("could not open the file BISECT_TERMS"));
+
+ res = fprintf(fp, "%s\n%s\n", bad, good);
+ res |= fclose(fp);
+ return (res < 0) ? -1 : 0;
+}
+
+static int is_expected_rev(const char *expected_hex)
+{
+ struct strbuf actual_hex = STRBUF_INIT;
+ int res = 0;
+ if (strbuf_read_file(&actual_hex, git_path_bisect_expected_rev(), 0) >= 40) {
+ strbuf_trim(&actual_hex);
+ res = !strcmp(actual_hex.buf, expected_hex);
+ }
+ strbuf_release(&actual_hex);
+ return res;
+}
+
+static void check_expected_revs(const char **revs, int rev_nr)
+{
+ int i;
+
+ for (i = 0; i < rev_nr; i++) {
+ if (!is_expected_rev(revs[i])) {
+ unlink_or_warn(git_path_bisect_ancestors_ok());
+ unlink_or_warn(git_path_bisect_expected_rev());
+ }
+ }
+}
+
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
{
- int next_all = 0;
+ enum {
+ NEXT_ALL = 1,
+ WRITE_TERMS,
+ BISECT_CLEAN_STATE,
+ CHECK_EXPECTED_REVS
+ } cmdmode = 0;
int no_checkout = 0;
struct option options[] = {
- OPT_BOOL(0, "next-all", &next_all,
- N_("perform 'git bisect next'")),
+ OPT_CMDMODE(0, "next-all", &cmdmode,
+ N_("perform 'git bisect next'"), NEXT_ALL),
+ OPT_CMDMODE(0, "write-terms", &cmdmode,
+ N_("write the terms to .git/BISECT_TERMS"), WRITE_TERMS),
+ OPT_CMDMODE(0, "bisect-clean-state", &cmdmode,
+ N_("cleanup the bisection state"), BISECT_CLEAN_STATE),
+ OPT_CMDMODE(0, "check-expected-revs", &cmdmode,
+ N_("check for expected revs"), CHECK_EXPECTED_REVS),
OPT_BOOL(0, "no-checkout", &no_checkout,
N_("update BISECT_HEAD instead of checking out the current commit")),
OPT_END()
argc = parse_options(argc, argv, prefix, options,
git_bisect_helper_usage, 0);
- if (!next_all)
+ if (!cmdmode)
usage_with_options(git_bisect_helper_usage, options);
- /* next-all */
- return bisect_next_all(prefix, no_checkout);
+ switch (cmdmode) {
+ case NEXT_ALL:
+ return bisect_next_all(prefix, no_checkout);
+ case WRITE_TERMS:
+ if (argc != 2)
+ return error(_("--write-terms requires two arguments"));
+ return write_terms(argv[0], argv[1]);
+ case BISECT_CLEAN_STATE:
+ if (argc != 0)
+ return error(_("--bisect-clean-state requires no arguments"));
+ return bisect_clean_state();
+ case CHECK_EXPECTED_REVS:
+ check_expected_revs(argv, argc);
+ return 0;
+ default:
+ return error("BUG: unknown subcommand '%d'", cmdmode);
+ }
+ return 0;
}
git_config(git_blame_config, &output_option);
init_revisions(&revs, NULL);
revs.date_mode = blame_date_mode;
- DIFF_OPT_SET(&revs.diffopt, ALLOW_TEXTCONV);
- DIFF_OPT_SET(&revs.diffopt, FOLLOW_RENAMES);
+ revs.diffopt.flags.allow_textconv = 1;
+ revs.diffopt.flags.follow_renames = 1;
save_commit_buffer = 0;
dashdash_pos = 0;
parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
}
parse_done:
- no_whole_file_rename = !DIFF_OPT_TST(&revs.diffopt, FOLLOW_RENAMES);
+ no_whole_file_rename = !revs.diffopt.flags.follow_renames;
xdl_opts |= revs.diffopt.xdl_opts & XDF_INDENT_HEURISTIC;
- DIFF_OPT_CLR(&revs.diffopt, FOLLOW_RENAMES);
+ revs.diffopt.flags.follow_renames = 0;
argc = parse_options_end(&ctx);
if (incremental || (output_option & OUTPUT_PORCELAIN)) {
}
blame_date_width -= 1; /* strip the null */
- if (DIFF_OPT_TST(&revs.diffopt, FIND_COPIES_HARDER))
+ if (revs.diffopt.flags.find_copies_harder)
opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
PICKAXE_BLAME_COPY_HARDER);
static int check_ref_format_branch(const char *arg)
{
struct strbuf sb = STRBUF_INIT;
+ const char *name;
int nongit;
setup_git_directory_gently(&nongit);
- if (strbuf_check_branch_ref(&sb, arg))
+ if (strbuf_check_branch_ref(&sb, arg) ||
+ !skip_prefix(sb.buf, "refs/heads/", &name))
die("'%s' is not a valid branch name", arg);
- printf("%s\n", sb.buf + 11);
+ printf("%s\n", name);
strbuf_release(&sb);
return 0;
}
NULL
};
-static struct lock_file lock_file;
-
static int option_parse_stage(const struct option *opt,
const char *arg, int unset)
{
int cmd_checkout_index(int argc, const char **argv, const char *prefix)
{
int i;
- int newfd = -1;
+ struct lock_file lock_file = LOCK_INIT;
int all = 0;
int read_from_stdin = 0;
int prefix_length;
if (index_opt && !state.base_dir_len && !to_tempfile) {
state.refresh_cache = 1;
state.istate = &the_index;
- newfd = hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
}
/* Check out named files first */
if (all)
checkout_all(prefix, prefix_length);
- if (0 <= newfd &&
+ if (is_lock_file_locked(&lock_file) &&
write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die("Unable to write new index file");
return 0;
struct object_id rev;
struct commit *head;
int errs = 0;
- struct lock_file *lock_file;
+ struct lock_file lock_file = LOCK_INIT;
if (opts->track != BRANCH_TRACK_UNSPECIFIED)
die(_("'%s' cannot be used with updating paths"), "--track");
return run_add_interactive(revision, "--patch=checkout",
&opts->pathspec);
- lock_file = xcalloc(1, sizeof(struct lock_file));
-
- hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
if (read_cache_preload(&opts->pathspec) < 0)
return error(_("index file corrupt"));
}
errs |= finish_delayed_checkout(&state);
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
read_ref_full("HEAD", 0, &rev, NULL);
int *writeout_error)
{
int ret;
- struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+ struct lock_file lock_file = LOCK_INIT;
- hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
if (read_cache_preload(NULL) < 0)
return error(_("index file corrupt"));
if (!cache_tree_fully_valid(active_cache_tree))
cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
if (!opts->force && !opts->quiet)
{
struct object_id oid;
char *head;
- struct lock_file *lock_file;
+ struct lock_file lock_file = LOCK_INIT;
struct unpack_trees_options opts;
struct tree *tree;
struct tree_desc t;
/* We need to be in the new work tree for the checkout */
setup_work_tree();
- lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
memset(&opts, 0, sizeof opts);
opts.update = 1;
if (unpack_trees(1, &t, &opts) < 0)
die(_("unable to checkout working tree"));
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static int config_commit_verbose = -1; /* unspecified */
static int no_post_rewrite, allow_empty_message;
-static char *untracked_files_arg, *force_date, *ignore_submodule_arg;
+static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
static char *sign_commit;
/*
static enum commit_whence whence;
static int sequencer_in_use;
static int use_editor = 1, include_status = 1;
-static int show_ignored_in_status, have_option_m;
+static int have_option_m;
static struct strbuf message = STRBUF_INIT;
static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
refresh_cache_or_die(refresh_flags);
- if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
+ if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to create temporary index"));
old_index_env = getenv(INDEX_ENVIRONMENT);
if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
if (reopen_lock_file(&index_lock) < 0)
die(_("unable to write index file"));
- if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
+ if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to update temporary index"));
} else
warning(_("Failed to update main cache tree"));
add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
refresh_cache_or_die(refresh_flags);
update_main_cache_tree(WRITE_TREE_SILENT);
- if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
+ if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to write new_index file"));
commit_style = COMMIT_NORMAL;
ret = get_lock_file_path(&index_lock);
add_remove_files(&partial);
refresh_cache(REFRESH_QUIET);
update_main_cache_tree(WRITE_TREE_SILENT);
- if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
+ if (write_locked_index(&the_index, &index_lock, 0))
die(_("unable to write new_index file"));
hold_lock_file_for_update(&false_lock,
add_remove_files(&partial);
refresh_cache(REFRESH_QUIET);
- if (write_locked_index(&the_index, &false_lock, CLOSE_LOCK))
+ if (write_locked_index(&the_index, &false_lock, 0))
die(_("unable to write temporary index file"));
discard_cache();
* submodules which were manually staged, which would
* be really confusing.
*/
- int diff_flags = DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+ struct diff_flags flags = DIFF_FLAGS_INIT;
+ flags.override_submodule_config = 1;
if (ignore_submodule_arg &&
!strcmp(ignore_submodule_arg, "all"))
- diff_flags |= DIFF_OPT_IGNORE_SUBMODULES;
- commitable = index_differs_from(parent, diff_flags, 1);
+ flags.ignore_submodules = 1;
+ commitable = index_differs_from(parent, &flags, 1);
}
}
strbuf_release(&committer_ident);
die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name);
}
+static void handle_ignored_arg(struct wt_status *s)
+{
+ if (!ignored_arg)
+ ; /* default already initialized */
+ else if (!strcmp(ignored_arg, "traditional"))
+ s->show_ignored_mode = SHOW_TRADITIONAL_IGNORED;
+ else if (!strcmp(ignored_arg, "no"))
+ s->show_ignored_mode = SHOW_NO_IGNORED;
+ else if (!strcmp(ignored_arg, "matching"))
+ s->show_ignored_mode = SHOW_MATCHING_IGNORED;
+ else
+ die(_("Invalid ignored mode '%s'"), ignored_arg);
+}
static void handle_untracked_files_arg(struct wt_status *s)
{
N_("mode"),
N_("show untracked files, optional modes: all, normal, no. (Default: all)"),
PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
- OPT_BOOL(0, "ignored", &show_ignored_in_status,
- N_("show ignored files")),
+ { OPTION_STRING, 0, "ignored", &ignored_arg,
+ N_("mode"),
+ N_("show ignored files, optional modes: traditional, matching, no. (Default: traditional)"),
+ PARSE_OPT_OPTARG, NULL, (intptr_t)"traditional" },
{ OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"),
N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"),
PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
finalize_deferred_config(&s);
handle_untracked_files_arg(&s);
- if (show_ignored_in_status)
- s.show_ignored_files = 1;
+ handle_ignored_arg(&s);
+
+ if (s.show_ignored_mode == SHOW_MATCHING_IGNORED &&
+ s.show_untracked_files == SHOW_NO_UNTRACKED_FILES)
+ die(_("Unsupported combination of ignored and untracked-files arguments"));
+
parse_pathspec(&s.pathspec, 0,
PATHSPEC_PREFER_FULL,
prefix, argv);
diff_setup_done(&rev.diffopt);
head = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
+ if (!head)
+ die_errno(_("unable to resolve HEAD after creating commit"));
if (!strcmp(head, "HEAD"))
head = _("detached HEAD");
else
#include "builtin.h"
#include "exec_cmd.h"
#include "parse-options.h"
+#include "revision.h"
#include "diff.h"
#include "hashmap.h"
#include "argv-array.h"
#include "run-command.h"
-#define SEEN (1u << 0)
#define MAX_TAGS (FLAG_BITS - 1)
static const char * const describe_usage[] = {
}
} else if (dirty) {
static struct lock_file index_lock;
- int fd;
+ struct rev_info revs;
+ struct argv_array args = ARGV_ARRAY_INIT;
+ int fd, result;
read_cache_preload(NULL);
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED,
if (0 <= fd)
update_index_if_able(&the_index, &index_lock);
- if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1,
- diff_index_args, prefix))
+ init_revisions(&revs, prefix);
+ argv_array_pushv(&args, diff_index_args);
+ if (setup_revisions(args.argc, args.argv, &revs, NULL) != 1)
+ BUG("malformed internal diff-index command line");
+ result = run_diff_index(&revs, 0);
+
+ if (!diff_result_code(&revs.diffopt, result))
suffix = NULL;
else
suffix = dirty;
!oidcmp(old_oid, new_oid) && (old_mode == new_mode))
return;
- if (DIFF_OPT_TST(opt, REVERSE_DIFF)) {
+ if (opt->flags.reverse_diff) {
SWAP(old_mode, new_mode);
SWAP(old_oid, new_oid);
SWAP(old_path, new_path);
static void refresh_index_quietly(void)
{
- struct lock_file *lock_file;
+ struct lock_file lock_file = LOCK_INIT;
int fd;
- lock_file = xcalloc(1, sizeof(struct lock_file));
- fd = hold_locked_index(lock_file, 0);
+ fd = hold_locked_index(&lock_file, 0);
if (fd < 0)
return;
discard_cache();
read_cache();
refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
- update_index_if_able(&the_index, lock_file);
+ update_index_if_able(&the_index, &lock_file);
}
static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
rev.diffopt.stat_graph_width = -1;
/* Default to let external and textconv be used */
- DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
- DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
+ rev.diffopt.flags.allow_external = 1;
+ rev.diffopt.flags.allow_textconv = 1;
if (nongit)
die(_("Not a git repository"));
diff_setup_done(&rev.diffopt);
}
- DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
+ rev.diffopt.flags.recursive = 1;
setup_diff_pager(&rev.diffopt);
if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 ||
write_locked_index(&wtindex, &lock, COMMIT_LOCK)) {
ret = error("could not write %s", buf.buf);
- rollback_lock_file(&lock);
goto finish;
}
changed_files(&wt_modified, buf.buf, workdir);
die("revision walk setup failed");
revs.diffopt.format_callback = show_filemodify;
revs.diffopt.format_callback_data = &paths_of_changed_objects;
- DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
+ revs.diffopt.flags.recursive = 1;
while ((commit = get_revision(&revs))) {
if (has_unshown_parent(commit)) {
add_object_array(&commit->object, NULL, &commits);
* store is no longer global and instead is a member of the repository
* object.
*/
+ grep_read_lock();
add_to_alternates_memory(submodule.objectdir);
+ grep_read_unlock();
if (oid) {
struct object *object;
if (fmt_pretty)
get_commit_format(fmt_pretty, rev);
if (default_follow)
- DIFF_OPT_SET(&rev->diffopt, DEFAULT_FOLLOW_RENAMES);
+ rev->diffopt.flags.default_follow_renames = 1;
rev->verbose_header = 1;
- DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
+ rev->diffopt.flags.recursive = 1;
rev->diffopt.stat_width = -1; /* use full terminal width */
rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */
rev->abbrev_commit = default_abbrev_commit;
rev->show_root_diff = default_show_root;
rev->subject_prefix = fmt_patch_subject_prefix;
rev->show_signature = default_show_signature;
- DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
+ rev->diffopt.flags.allow_textconv = 1;
if (default_date_mode)
parse_date_format(default_date_mode, &rev->date_mode);
- rev->diffopt.touched_flags = 0;
}
static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
init_display_notes(&rev->notes_opt);
if (rev->diffopt.pickaxe || rev->diffopt.filter ||
- DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES))
+ rev->diffopt.flags.follow_renames)
rev->always_show_header = 0;
if (source)
fclose(rev->diffopt.file);
if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
- DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
+ rev->diffopt.flags.check_failed) {
return 02;
}
return diff_result_code(&rev->diffopt, 0);
unsigned long size;
fflush(rev->diffopt.file);
- if (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||
- !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
+ if (!rev->diffopt.flags.textconv_set_via_cmdline ||
+ !rev->diffopt.flags.allow_textconv)
return stream_blob_to_fd(1, oid, NULL, 0);
if (get_oid_with_context(obj_name, GET_OID_RECORD_PATH,
static void log_setup_revisions_tweak(struct rev_info *rev,
struct setup_revision_opt *opt)
{
- if (DIFF_OPT_TST(&rev->diffopt, DEFAULT_FOLLOW_RENAMES) &&
+ if (rev->diffopt.flags.default_follow_renames &&
rev->prune_data.nr == 1)
- DIFF_OPT_SET(&rev->diffopt, FOLLOW_RENAMES);
+ rev->diffopt.flags.follow_renames = 1;
/* Turn --cc/-c into -p --cc/-c when -p was not given */
if (!rev->diffopt.output_format && rev->combine_merges)
return;
diff_setup(&diffopt);
- DIFF_OPT_SET(&diffopt, RECURSIVE);
+ diffopt.flags.recursive = 1;
diff_setup_done(&diffopt);
oidcpy(&bases->base_commit, &base->object.oid);
rev.verbose_header = 1;
rev.diff = 1;
rev.max_parents = 1;
- DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
+ rev.diffopt.flags.recursive = 1;
rev.subject_prefix = fmt_patch_subject_prefix;
memset(&s_r_opt, 0, sizeof(s_r_opt));
s_r_opt.def = "HEAD";
rev.zero_commit = zero_commit;
- if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
- DIFF_OPT_SET(&rev.diffopt, BINARY);
+ if (!rev.diffopt.flags.text && !no_binary_diff)
+ rev.diffopt.flags.binary = 1;
if (rev.show_notes)
init_display_notes(&rev.notes_opt);
*/
#include "git-compat-util.h"
#include "builtin.h"
+#include "diff.h"
static const char builtin_merge_ours_usage[] =
"git merge-ours <base>... -- HEAD <remote>...";
-static const char *diff_index_args[] = {
- "diff-index", "--quiet", "--cached", "HEAD", "--", NULL
-};
-#define NARGS (ARRAY_SIZE(diff_index_args) - 1)
-
int cmd_merge_ours(int argc, const char **argv, const char *prefix)
{
if (argc == 2 && !strcmp(argv[1], "-h"))
usage(builtin_merge_ours_usage);
/*
- * We need to exit with 2 if the index does not match our HEAD tree,
- * because the current index is what we will be committing as the
- * merge result.
+ * The contents of the current index becomes the tree we
+ * commit. The index must match HEAD, or this merge cannot go
+ * through.
*/
- if (cmd_diff_index(NARGS, diff_index_args, prefix))
+ if (read_cache() < 0)
+ die_errno("read_cache failed");
+ if (index_differs_from("HEAD", NULL, 0))
exit(2);
exit(0);
}
static enum rebase_type opt_rebase = -1;
static char *opt_diffstat;
static char *opt_log;
+static char *opt_signoff;
static char *opt_squash;
static char *opt_commit;
static char *opt_edit;
OPT_PASSTHRU(0, "log", &opt_log, N_("n"),
N_("add (at most <n>) entries from shortlog to merge commit message"),
PARSE_OPT_OPTARG),
+ OPT_PASSTHRU(0, "signoff", &opt_signoff, NULL,
+ N_("add Signed-off-by:"),
+ PARSE_OPT_OPTARG),
OPT_PASSTHRU(0, "squash", &opt_squash, NULL,
N_("create a single commit instead of doing a merge"),
PARSE_OPT_NOARG),
argv_array_push(&args, opt_diffstat);
if (opt_log)
argv_array_push(&args, opt_log);
+ if (opt_signoff)
+ argv_array_push(&args, opt_signoff);
if (opt_squash)
argv_array_push(&args, opt_squash);
if (opt_commit)
static int refspec_nr;
static int refspec_alloc;
+static struct string_list push_options_config = STRING_LIST_INIT_DUP;
+
static void add_refspec(const char *ref)
{
refspec_nr++;
int val = git_config_bool(k, v) ?
RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF;
recurse_submodules = val;
+ } else if (!strcmp(k, "push.pushoption")) {
+ if (!v)
+ return config_error_nonbool(k);
+ else
+ if (!*v)
+ string_list_clear(&push_options_config, 0);
+ else
+ string_list_append(&push_options_config, v);
+ return 0;
}
return git_default_config(k, v, NULL);
int push_cert = -1;
int rc;
const char *repo = NULL; /* default repository */
- struct string_list push_options = STRING_LIST_INIT_DUP;
+ struct string_list push_options_cmdline = STRING_LIST_INIT_DUP;
+ struct string_list *push_options;
const struct string_list_item *item;
struct option options[] = {
0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
PARSE_OPT_OPTARG, option_parse_push_signed },
OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC),
- OPT_STRING_LIST('o', "push-option", &push_options, N_("server-specific"), N_("option to transmit")),
+ OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")),
OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
TRANSPORT_FAMILY_IPV4),
OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
packet_trace_identity("push");
git_config(git_push_config, &flags);
argc = parse_options(argc, argv, prefix, options, push_usage, 0);
+ push_options = (push_options_cmdline.nr
+ ? &push_options_cmdline
+ : &push_options_config);
set_push_cert_flags(&flags, push_cert);
if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
set_refspecs(argv + 1, argc - 1, repo);
}
- for_each_string_list_item(item, &push_options)
+ for_each_string_list_item(item, push_options)
if (strchr(item->string, '\n'))
die(_("push options must not have new line characters"));
- rc = do_push(repo, flags, &push_options);
- string_list_clear(&push_options, 0);
+ rc = do_push(repo, flags, push_options);
+ string_list_clear(&push_options_cmdline, 0);
+ string_list_clear(&push_options_config, 0);
if (rc == -1)
usage_with_options(push_usage, options);
else
item = string_list_append(rename->remote_branches, xstrdup(refname));
symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
NULL, &flag);
- if (flag & REF_ISSYMREF)
+ if (symref && (flag & REF_ISSYMREF))
item->util = xstrdup(symref);
else
item->util = NULL;
opt.output_format = DIFF_FORMAT_CALLBACK;
opt.format_callback = update_index_from_diff;
opt.format_callback_data = &intent_to_add;
- opt.flags |= DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG;
+ opt.flags.override_submodule_config = 1;
if (do_diff_cache(tree_oid, &opt))
return 1;
if (revs.bisect)
bisect_list = 1;
- if (DIFF_OPT_TST(&revs.diffopt, QUICK))
+ if (revs.diffopt.flags.quick)
info.flags |= REV_LIST_QUIET;
for (i = 1 ; i < argc; i++) {
const char *arg = argv[i];
if (bisect_list) {
int reaches = reaches, all = all;
- revs.commits = find_bisection(revs.commits, &reaches, &all,
- bisect_find_all);
+ find_bisection(&revs.commits, &reaches, &all, bisect_find_all);
if (bisect_show_vars)
return show_bisect_vars(&info, reaches, all);
#include "remote.h"
#include "refs.h"
#include "connect.h"
+#include "revision.h"
+#include "diffcore.h"
+#include "diff.h"
+
+#define OPT_QUIET (1 << 0)
+#define OPT_CACHED (1 << 1)
+#define OPT_RECURSIVE (1 << 2)
+
+typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
+ void *cb_data);
static char *get_default_remote(void)
{
return 0;
}
+/* the result should be freed by the caller. */
+static char *get_submodule_displaypath(const char *path, const char *prefix)
+{
+ const char *super_prefix = get_super_prefix();
+
+ if (prefix && super_prefix) {
+ BUG("cannot have prefix '%s' and superprefix '%s'",
+ prefix, super_prefix);
+ } else if (prefix) {
+ struct strbuf sb = STRBUF_INIT;
+ char *displaypath = xstrdup(relative_path(path, prefix, &sb));
+ strbuf_release(&sb);
+ return displaypath;
+ } else if (super_prefix) {
+ return xstrfmt("%s%s", super_prefix, path);
+ } else {
+ return xstrdup(path);
+ }
+}
+
+static char *compute_rev_name(const char *sub_path, const char* object_id)
+{
+ struct strbuf sb = STRBUF_INIT;
+ const char ***d;
+
+ static const char *describe_bare[] = { NULL };
+
+ static const char *describe_tags[] = { "--tags", NULL };
+
+ static const char *describe_contains[] = { "--contains", NULL };
+
+ static const char *describe_all_always[] = { "--all", "--always", NULL };
+
+ static const char **describe_argv[] = { describe_bare, describe_tags,
+ describe_contains,
+ describe_all_always, NULL };
+
+ for (d = describe_argv; *d; d++) {
+ struct child_process cp = CHILD_PROCESS_INIT;
+ prepare_submodule_repo_env(&cp.env_array);
+ cp.dir = sub_path;
+ cp.git_cmd = 1;
+ cp.no_stderr = 1;
+
+ argv_array_push(&cp.args, "describe");
+ argv_array_pushv(&cp.args, *d);
+ argv_array_push(&cp.args, object_id);
+
+ if (!capture_command(&cp, &sb, 0)) {
+ strbuf_strip_suffix(&sb, "\n");
+ return strbuf_detach(&sb, NULL);
+ }
+ }
+
+ strbuf_release(&sb);
+ return NULL;
+}
+
struct module_list {
const struct cache_entry **entries;
int alloc, nr;
return 0;
}
-static void init_submodule(const char *path, const char *prefix, int quiet)
+static void for_each_listed_submodule(const struct module_list *list,
+ each_submodule_fn fn, void *cb_data)
+{
+ int i;
+ for (i = 0; i < list->nr; i++)
+ fn(list->entries[i], cb_data);
+}
+
+struct init_cb {
+ const char *prefix;
+ unsigned int flags;
+};
+
+#define INIT_CB_INIT { NULL, 0 }
+
+static void init_submodule(const char *path, const char *prefix,
+ unsigned int flags)
{
const struct submodule *sub;
struct strbuf sb = STRBUF_INIT;
char *upd = NULL, *url = NULL, *displaypath;
- if (prefix && get_super_prefix())
- die("BUG: cannot have prefix and superprefix");
- else if (prefix)
- displaypath = xstrdup(relative_path(path, prefix, &sb));
- else if (get_super_prefix()) {
- strbuf_addf(&sb, "%s%s", get_super_prefix(), path);
- displaypath = strbuf_detach(&sb, NULL);
- } else
- displaypath = xstrdup(path);
+ displaypath = get_submodule_displaypath(path, prefix);
sub = submodule_from_path(&null_oid, path);
* Set active flag for the submodule being initialized
*/
if (!is_submodule_active(the_repository, path)) {
- strbuf_reset(&sb);
strbuf_addf(&sb, "submodule.%s.active", sub->name);
git_config_set_gently(sb.buf, "true");
+ strbuf_reset(&sb);
}
/*
* To look up the url in .git/config, we must not fall back to
* .gitmodules, so look it up directly.
*/
- strbuf_reset(&sb);
strbuf_addf(&sb, "submodule.%s.url", sub->name);
if (git_config_get_string(sb.buf, &url)) {
if (!sub->url)
if (git_config_set_gently(sb.buf, url))
die(_("Failed to register url for submodule path '%s'"),
displaypath);
- if (!quiet)
+ if (!(flags & OPT_QUIET))
fprintf(stderr,
_("Submodule '%s' (%s) registered for path '%s'\n"),
sub->name, url, displaypath);
}
+ strbuf_reset(&sb);
/* Copy "update" setting when it is not set yet */
- strbuf_reset(&sb);
strbuf_addf(&sb, "submodule.%s.update", sub->name);
if (git_config_get_string(sb.buf, &upd) &&
sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
free(upd);
}
+static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data)
+{
+ struct init_cb *info = cb_data;
+ init_submodule(list_item->name, info->prefix, info->flags);
+}
+
static int module_init(int argc, const char **argv, const char *prefix)
{
+ struct init_cb info = INIT_CB_INIT;
struct pathspec pathspec;
struct module_list list = MODULE_LIST_INIT;
int quiet = 0;
- int i;
struct option module_init_options[] = {
OPT__QUIET(&quiet, N_("Suppress output for initializing a submodule")),
if (!argc && git_config_get_value_multi("submodule.active"))
module_list_active(&list);
- for (i = 0; i < list.nr; i++)
- init_submodule(list.entries[i]->name, prefix, quiet);
+ info.prefix = prefix;
+ if (quiet)
+ info.flags |= OPT_QUIET;
+
+ for_each_listed_submodule(&list, init_submodule_cb, &info);
+
+ return 0;
+}
+
+struct status_cb {
+ const char *prefix;
+ unsigned int flags;
+};
+
+#define STATUS_CB_INIT { NULL, 0 }
+
+static void print_status(unsigned int flags, char state, const char *path,
+ const struct object_id *oid, const char *displaypath)
+{
+ if (flags & OPT_QUIET)
+ return;
+
+ printf("%c%s %s", state, oid_to_hex(oid), displaypath);
+
+ if (state == ' ' || state == '+')
+ printf(" (%s)", compute_rev_name(path, oid_to_hex(oid)));
+
+ printf("\n");
+}
+
+static int handle_submodule_head_ref(const char *refname,
+ const struct object_id *oid, int flags,
+ void *cb_data)
+{
+ struct object_id *output = cb_data;
+ if (oid)
+ oidcpy(output, oid);
+
+ return 0;
+}
+
+static void status_submodule(const char *path, const struct object_id *ce_oid,
+ unsigned int ce_flags, const char *prefix,
+ unsigned int flags)
+{
+ char *displaypath;
+ struct argv_array diff_files_args = ARGV_ARRAY_INIT;
+ struct rev_info rev;
+ int diff_files_result;
+
+ if (!submodule_from_path(&null_oid, path))
+ die(_("no submodule mapping found in .gitmodules for path '%s'"),
+ path);
+
+ displaypath = get_submodule_displaypath(path, prefix);
+
+ if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
+ print_status(flags, 'U', path, &null_oid, displaypath);
+ goto cleanup;
+ }
+
+ if (!is_submodule_active(the_repository, path)) {
+ print_status(flags, '-', path, ce_oid, displaypath);
+ goto cleanup;
+ }
+
+ argv_array_pushl(&diff_files_args, "diff-files",
+ "--ignore-submodules=dirty", "--quiet", "--",
+ path, NULL);
+
+ git_config(git_diff_basic_config, NULL);
+ init_revisions(&rev, prefix);
+ rev.abbrev = 0;
+ diff_files_args.argc = setup_revisions(diff_files_args.argc,
+ diff_files_args.argv,
+ &rev, NULL);
+ diff_files_result = run_diff_files(&rev, 0);
+
+ if (!diff_result_code(&rev.diffopt, diff_files_result)) {
+ print_status(flags, ' ', path, ce_oid,
+ displaypath);
+ } else if (!(flags & OPT_CACHED)) {
+ struct object_id oid;
+
+ if (refs_head_ref(get_submodule_ref_store(path),
+ handle_submodule_head_ref, &oid))
+ die(_("could not resolve HEAD ref inside the"
+ "submodule '%s'"), path);
+
+ print_status(flags, '+', path, &oid, displaypath);
+ } else {
+ print_status(flags, '+', path, ce_oid, displaypath);
+ }
+
+ if (flags & OPT_RECURSIVE) {
+ struct child_process cpr = CHILD_PROCESS_INIT;
+
+ cpr.git_cmd = 1;
+ cpr.dir = path;
+ prepare_submodule_repo_env(&cpr.env_array);
+
+ argv_array_push(&cpr.args, "--super-prefix");
+ argv_array_pushf(&cpr.args, "%s/", displaypath);
+ argv_array_pushl(&cpr.args, "submodule--helper", "status",
+ "--recursive", NULL);
+
+ if (flags & OPT_CACHED)
+ argv_array_push(&cpr.args, "--cached");
+
+ if (flags & OPT_QUIET)
+ argv_array_push(&cpr.args, "--quiet");
+
+ if (run_command(&cpr))
+ die(_("failed to recurse into submodule '%s'"), path);
+ }
+
+cleanup:
+ argv_array_clear(&diff_files_args);
+ free(displaypath);
+}
+
+static void status_submodule_cb(const struct cache_entry *list_item,
+ void *cb_data)
+{
+ struct status_cb *info = cb_data;
+ status_submodule(list_item->name, &list_item->oid, list_item->ce_flags,
+ info->prefix, info->flags);
+}
+
+static int module_status(int argc, const char **argv, const char *prefix)
+{
+ struct status_cb info = STATUS_CB_INIT;
+ struct pathspec pathspec;
+ struct module_list list = MODULE_LIST_INIT;
+ int quiet = 0;
+
+ struct option module_status_options[] = {
+ OPT__QUIET(&quiet, N_("Suppress submodule status output")),
+ OPT_BIT(0, "cached", &info.flags, N_("Use commit stored in the index instead of the one stored in the submodule HEAD"), OPT_CACHED),
+ OPT_BIT(0, "recursive", &info.flags, N_("recurse into nested submodules"), OPT_RECURSIVE),
+ OPT_END()
+ };
+
+ const char *const git_submodule_helper_usage[] = {
+ N_("git submodule status [--quiet] [--cached] [--recursive] [<path>...]"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, module_status_options,
+ git_submodule_helper_usage, 0);
+
+ if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
+ return 1;
+
+ info.prefix = prefix;
+ if (quiet)
+ info.flags |= OPT_QUIET;
+
+ for_each_listed_submodule(&list, status_submodule_cb, &info);
return 0;
}
{"resolve-relative-url", resolve_relative_url, 0},
{"resolve-relative-url-test", resolve_relative_url_test, 0},
{"init", module_init, SUPPORT_SUPER_PREFIX},
+ {"status", module_status, SUPPORT_SUPER_PREFIX},
{"remote-branch", resolve_remote_submodule_branch, 0},
{"push-check", push_check, 0},
{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, const char *index_path, int flags, const char *prefix)
{
- int entries, was_valid, newfd;
+ int entries, was_valid;
struct lock_file lock_file = LOCK_INIT;
int ret = 0;
- newfd = hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);
+ hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);
entries = read_index_from(index_state, index_path);
if (entries < 0) {
ret = WRITE_TREE_UNMERGED_INDEX;
goto out;
}
- if (0 <= newfd) {
- if (!write_locked_index(index_state, &lock_file, COMMIT_LOCK))
- newfd = -1;
- }
+ write_locked_index(index_state, &lock_file, COMMIT_LOCK);
/* Not being able to write is fine -- we are only interested
* in updating the cache-tree part, and if the next caller
* ends up using the old index with unupdated cache-tree part
hashcpy(sha1, index_state->cache_tree->oid.hash);
out:
- if (0 <= newfd)
- rollback_lock_file(&lock_file);
+ rollback_lock_file(&lock_file);
return ret;
}
extern int read_index_from(struct index_state *, const char *path);
extern int is_index_unborn(struct index_state *);
extern int read_index_unmerged(struct index_state *);
+
+/* For use with `write_locked_index()`. */
#define COMMIT_LOCK (1 << 0)
-#define CLOSE_LOCK (1 << 1)
+
+/*
+ * Write the index while holding an already-taken lock. Close the lock,
+ * and if `COMMIT_LOCK` is given, commit it.
+ *
+ * Unless a split index is in use, write the index into the lockfile.
+ *
+ * With a split index, write the shared index to a temporary file,
+ * adjust its permissions and rename it into place, then write the
+ * split index to the lockfile. If the temporary file for the shared
+ * index cannot be created, fall back to the behavior described in
+ * the previous paragraph.
+ *
+ * With `COMMIT_LOCK`, the lock is always committed or rolled back.
+ * Without it, the lock is closed, but neither committed nor rolled
+ * back.
+ */
extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
+
extern int discard_index(struct index_state *);
extern void move_index_extensions(struct index_state *dst, struct index_state *src);
extern int unmerged_index(const struct index_state *);
extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
+/*
+ * Opportunistically update the index but do not complain if we can't.
+ * The lockfile is always committed or rolled back.
+ */
extern void update_index_if_able(struct index_state *, struct lock_file *);
extern int hold_locked_index(struct lock_file *, int);
extern int get_sha1_hex(const char *hex, unsigned char *sha1);
extern int get_oid_hex(const char *hex, struct object_id *sha1);
+/*
+ * Read `len` pairs of hexadecimal digits from `hex` and write the
+ * values to `binary` as `len` bytes. Return 0 on success, or -1 if
+ * the input does not consist of hex digits).
+ */
+extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
+
/*
* Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
* and writes the NUL-terminated output to the buffer `out`, which must be at
linux)
export GIT_TEST_HTTPD=YesPlease
- mkdir --parents custom/p4
- pushd custom/p4
+ mkdir --parents "$P4_PATH"
+ pushd "$P4_PATH"
wget --quiet "$P4WHENCE/bin.linux26x86_64/p4d"
wget --quiet "$P4WHENCE/bin.linux26x86_64/p4"
chmod u+x p4d
chmod u+x p4
- export PATH="$(pwd):$PATH"
popd
- mkdir --parents custom/git-lfs
- pushd custom/git-lfs
+ mkdir --parents "$GIT_LFS_PATH"
+ pushd "$GIT_LFS_PATH"
wget --quiet "$LFSWHENCE/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
tar --extract --gunzip --file "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz"
cp git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs .
- export PATH="$(pwd):$PATH"
popd
;;
osx)
set -e
skip_branch_tip_with_tag
+
+case "${TRAVIS_OS_NAME:-linux}" in
+linux)
+ P4_PATH="$(pwd)/custom/p4"
+ GIT_LFS_PATH="$(pwd)/custom/git-lfs"
+ export PATH="$GIT_LFS_PATH:$P4_PATH:$PATH"
+ ;;
+esac
if (stdout_is_tty < 0)
stdout_is_tty = isatty(1);
*colopts &= ~COL_ENABLE_MASK;
- if (stdout_is_tty)
+ if (stdout_is_tty || pager_in_use())
*colopts |= COL_ENABLED;
}
return 0;
int show_file_header)
{
struct diff_options *opt = &rev->diffopt;
- int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? GIT_SHA1_HEXSZ : DEFAULT_ABBREV;
+ int abbrev = opt->flags.full_index ? GIT_SHA1_HEXSZ : DEFAULT_ABBREV;
const char *a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
const char *b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
const char *c_meta = diff_get_color_opt(opt, DIFF_METAINFO);
userdiff = userdiff_find_by_path(elem->path);
if (!userdiff)
userdiff = userdiff_find_by_name("default");
- if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV))
+ if (opt->flags.allow_textconv)
textconv = userdiff_get_textconv(userdiff);
/* Read the result of merge first */
diffopts = *opt;
copy_pathspec(&diffopts.pathspec, &opt->pathspec);
- DIFF_OPT_SET(&diffopts, RECURSIVE);
- DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL);
+ diffopts.flags.recursive = 1;
+ diffopts.flags.allow_external = 0;
/* find set of paths that everybody touches
*
* NOTE please keep this semantically in sync with diffcore_std()
*/
need_generic_pathscan = opt->skip_stat_unmatch ||
- DIFF_OPT_TST(opt, FOLLOW_RENAMES) ||
+ opt->flags.follow_renames ||
opt->break_opt != -1 ||
opt->detect_rename ||
opt->pickaxe ||
return memcpy(malloc_startup(len), buffer, len);
}
+static void maybe_redirect_std_handle(const wchar_t *key, DWORD std_id, int fd,
+ DWORD desired_access, DWORD flags)
+{
+ DWORD create_flag = fd ? OPEN_ALWAYS : OPEN_EXISTING;
+ wchar_t buf[MAX_PATH];
+ DWORD max = ARRAY_SIZE(buf);
+ HANDLE handle;
+ DWORD ret = GetEnvironmentVariableW(key, buf, max);
+
+ if (!ret || ret >= max)
+ return;
+
+ /* make sure this does not leak into child processes */
+ SetEnvironmentVariableW(key, NULL);
+ if (!wcscmp(buf, L"off")) {
+ close(fd);
+ handle = GetStdHandle(std_id);
+ if (handle != INVALID_HANDLE_VALUE)
+ CloseHandle(handle);
+ return;
+ }
+ if (std_id == STD_ERROR_HANDLE && !wcscmp(buf, L"2>&1")) {
+ handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (handle == INVALID_HANDLE_VALUE) {
+ close(fd);
+ handle = GetStdHandle(std_id);
+ if (handle != INVALID_HANDLE_VALUE)
+ CloseHandle(handle);
+ } else {
+ int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
+ SetStdHandle(std_id, handle);
+ dup2(new_fd, fd);
+ /* do *not* close the new_fd: that would close stdout */
+ }
+ return;
+ }
+ handle = CreateFileW(buf, desired_access, 0, NULL, create_flag,
+ flags, NULL);
+ if (handle != INVALID_HANDLE_VALUE) {
+ int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
+ SetStdHandle(std_id, handle);
+ dup2(new_fd, fd);
+ close(new_fd);
+ }
+}
+
+static void maybe_redirect_std_handles(void)
+{
+ maybe_redirect_std_handle(L"GIT_REDIRECT_STDIN", STD_INPUT_HANDLE, 0,
+ GENERIC_READ, FILE_ATTRIBUTE_NORMAL);
+ maybe_redirect_std_handle(L"GIT_REDIRECT_STDOUT", STD_OUTPUT_HANDLE, 1,
+ GENERIC_WRITE, FILE_ATTRIBUTE_NORMAL);
+ maybe_redirect_std_handle(L"GIT_REDIRECT_STDERR", STD_ERROR_HANDLE, 2,
+ GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
+}
+
void mingw_startup(void)
{
int i, maxlen, argc;
wchar_t **wenv, **wargv;
_startupinfo si;
+ maybe_redirect_std_handles();
+
/* get wide char arguments and environment */
si.newmode = 0;
if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
{
int ret = 0, remove = 0;
char *filename_buf = NULL;
- struct lock_file *lock;
+ struct lock_file lock = LOCK_INIT;
int out_fd;
char buf[1024];
FILE *config_file = NULL;
if (!config_filename)
config_filename = filename_buf = git_pathdup("config");
- lock = xcalloc(1, sizeof(struct lock_file));
- out_fd = hold_lock_file_for_update(lock, config_filename, 0);
+ out_fd = hold_lock_file_for_update(&lock, config_filename, 0);
if (out_fd < 0) {
ret = error("could not lock config file %s", config_filename);
goto out;
goto out;
}
- if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
+ if (chmod(get_lock_file_path(&lock), st.st_mode & 07777) < 0) {
ret = error_errno("chmod on %s failed",
- get_lock_file_path(lock));
+ get_lock_file_path(&lock));
goto out;
}
*/
if (copystr.len > 0) {
if (write_in_full(out_fd, copystr.buf, copystr.len) != copystr.len) {
- ret = write_error(get_lock_file_path(lock));
+ ret = write_error(get_lock_file_path(&lock));
goto out;
}
strbuf_reset(©str);
store.baselen = strlen(new_name);
if (!copy) {
if (write_section(out_fd, new_name) < 0) {
- ret = write_error(get_lock_file_path(lock));
+ ret = write_error(get_lock_file_path(&lock));
goto out;
}
/*
}
if (write_in_full(out_fd, output, length) < 0) {
- ret = write_error(get_lock_file_path(lock));
+ ret = write_error(get_lock_file_path(&lock));
goto out;
}
}
*/
if (copystr.len > 0) {
if (write_in_full(out_fd, copystr.buf, copystr.len) != copystr.len) {
- ret = write_error(get_lock_file_path(lock));
+ ret = write_error(get_lock_file_path(&lock));
goto out;
}
strbuf_reset(©str);
fclose(config_file);
config_file = NULL;
commit_and_out:
- if (commit_lock_file(lock) < 0)
+ if (commit_lock_file(&lock) < 0)
ret = error_errno("could not write config file %s",
config_filename);
out:
if (config_file)
fclose(config_file);
- rollback_lock_file(lock);
+ rollback_lock_file(&lock);
out_no_rollback:
free(filename_buf);
return ret;
--*)
__gitcomp "
--quiet --ours --theirs --track --no-track --merge
- --conflict= --orphan --patch
+ --conflict= --orphan --patch --detach --ignore-skip-worktree-bits
+ --recurse-submodules --no-recurse-submodules
"
;;
*)
advice.rmHints
advice.statusHints
advice.statusUoption
+ advice.ignoredHook
alias.
am.keepcr
am.threeWay
items = secret_service_search_sync(service,
SECRET_SCHEMA_COMPAT_NETWORK,
attributes,
- SECRET_SEARCH_LOAD_SECRETS,
+ SECRET_SEARCH_LOAD_SECRETS | SECRET_SEARCH_UNLOCK,
NULL,
&error);
g_hash_table_unref(attributes);
static void write_item(const char *what, LPCWSTR wbuf, int wlen)
{
char *buf;
+
+ if (!wbuf || !wlen) {
+ printf("%s=\n", what);
+ return;
+ }
+
int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, wlen, NULL, 0, NULL,
FALSE);
buf = xmalloc(len);
static int match_cred(const CREDENTIALW *cred)
{
LPCWSTR target = cred->TargetName;
- if (wusername && wcscmp(wusername, cred->UserName))
+ if (wusername && wcscmp(wusername, cred->UserName ? cred->UserName : L""))
return 0;
return match_part(&target, L"git", L":") &&
for (i = 0; i < num_creds; ++i)
if (match_cred(creds[i])) {
write_item("username", creds[i]->UserName,
- wcslen(creds[i]->UserName));
+ creds[i]->UserName ? wcslen(creds[i]->UserName) : 0);
write_item("password",
(LPCWSTR)creds[i]->CredentialBlob,
creds[i]->CredentialBlobSize / sizeof(WCHAR));
use 5.008;
use strict;
+use POSIX;
use Git;
BEGIN {
$filename =~ s/ /_/g;
# Decode forbidden characters encoded in clean_filename
$filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf('%c', hex($1))/ge;
- return $filename;
+ return substr($filename, 0, NAME_MAX-length('.mw'));
}
sub connect_maybe {
{
int changed = ce_match_stat(ce, st, ce_option);
if (S_ISGITLINK(ce->ce_mode)) {
- unsigned orig_flags = diffopt->flags;
- if (!DIFF_OPT_TST(diffopt, OVERRIDE_SUBMODULE_CONFIG))
+ struct diff_flags orig_flags = diffopt->flags;
+ if (!diffopt->flags.override_submodule_config)
set_diffopt_flags_from_submodule_config(diffopt, ce->name);
- if (DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES))
+ if (diffopt->flags.ignore_submodules)
changed = 0;
- else if (!DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES)
- && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES)))
- *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
+ else if (!diffopt->flags.ignore_dirty_submodules &&
+ (!changed || diffopt->flags.dirty_submodules))
+ *dirty_submodule = is_submodule_modified(ce->name,
+ diffopt->flags.ignore_untracked_in_submodules);
diffopt->flags = orig_flags;
}
return changed;
if (!changed && !dirty_submodule) {
ce_mark_uptodate(ce);
- if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
+ if (!revs->diffopt.flags.find_copies_harder)
continue;
}
oldmode = ce->ce_mode;
oldmode = old->ce_mode;
if (mode == oldmode && !oidcmp(oid, &old->oid) && !dirty_submodule &&
- !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
+ !revs->diffopt.flags.find_copies_harder)
return 0;
diff_change(&revs->diffopt, oldmode, mode,
opts.head_idx = 1;
opts.index_only = cached;
opts.diff_index_cached = (cached &&
- !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER));
+ !revs->diffopt.flags.find_copies_harder);
opts.merge = 1;
opts.fn = oneway_diff;
opts.unpack_data = revs;
return 0;
}
-int index_differs_from(const char *def, int diff_flags,
+int index_differs_from(const char *def, const struct diff_flags *flags,
int ita_invisible_in_index)
{
struct rev_info rev;
memset(&opt, 0, sizeof(opt));
opt.def = def;
setup_revisions(0, NULL, &rev, &opt);
- DIFF_OPT_SET(&rev.diffopt, QUICK);
- DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
- rev.diffopt.flags |= diff_flags;
+ rev.diffopt.flags.quick = 1;
+ rev.diffopt.flags.exit_with_status = 1;
+ if (flags)
+ diff_flags_or(&rev.diffopt.flags, flags);
rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
run_diff_index(&rev, 1);
object_array_clear(&rev.pending);
- return (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0);
+ return (rev.diffopt.flags.has_changes != 0);
}
} else {
struct diff_filespec *d1, *d2;
- if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+ if (o->flags.reverse_diff) {
SWAP(mode1, mode2);
SWAP(name1, name2);
}
if (!revs->diffopt.output_format)
revs->diffopt.output_format = DIFF_FORMAT_PATCH;
- DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
+ revs->diffopt.flags.no_index = 1;
- DIFF_OPT_SET(&revs->diffopt, RELATIVE_NAME);
+ revs->diffopt.flags.relative_name = 1;
revs->diffopt.prefix = prefix;
revs->max_count = -2;
diff_setup_done(&revs->diffopt);
setup_diff_pager(&revs->diffopt);
- DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
+ revs->diffopt.flags.exit_with_status = 1;
if (queue_diff(&revs->diffopt, paths[0], paths[1]))
exit(1);
for (i = 0; i < params.nr; i++) {
const char *p = params.items[i].string;
if (!strcmp(p, "changes")) {
- DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
- DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
+ options->flags.dirstat_by_line = 0;
+ options->flags.dirstat_by_file = 0;
} else if (!strcmp(p, "lines")) {
- DIFF_OPT_SET(options, DIRSTAT_BY_LINE);
- DIFF_OPT_CLR(options, DIRSTAT_BY_FILE);
+ options->flags.dirstat_by_line = 1;
+ options->flags.dirstat_by_file = 0;
} else if (!strcmp(p, "files")) {
- DIFF_OPT_CLR(options, DIRSTAT_BY_LINE);
- DIFF_OPT_SET(options, DIRSTAT_BY_FILE);
+ options->flags.dirstat_by_line = 0;
+ options->flags.dirstat_by_file = 1;
} else if (!strcmp(p, "noncumulative")) {
- DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);
+ options->flags.dirstat_cumulative = 0;
} else if (!strcmp(p, "cumulative")) {
- DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);
+ options->flags.dirstat_cumulative = 1;
} else if (isdigit(*p)) {
char *end;
int permille = strtoul(p, &end, 10) * 10;
struct moved_entry *next_line;
};
-static int next_byte(const char **cp, const char **endp,
- const struct diff_options *diffopt)
-{
- int retval;
-
- if (*cp > *endp)
- return -1;
-
- if (isspace(**cp)) {
- if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE_CHANGE)) {
- while (*cp < *endp && isspace(**cp))
- (*cp)++;
- /*
- * After skipping a couple of whitespaces,
- * we still have to account for one space.
- */
- return (int)' ';
- }
-
- if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE)) {
- while (*cp < *endp && isspace(**cp))
- (*cp)++;
- /* return the first non-ws character via the usual below */
- }
- }
-
- retval = (unsigned char)(**cp);
- (*cp)++;
- return retval;
-}
-
static int moved_entry_cmp(const struct diff_options *diffopt,
const struct moved_entry *a,
const struct moved_entry *b,
const void *keydata)
{
- const char *ap = a->es->line, *ae = a->es->line + a->es->len;
- const char *bp = b->es->line, *be = b->es->line + b->es->len;
-
- if (!(diffopt->xdl_opts & XDF_WHITESPACE_FLAGS))
- return a->es->len != b->es->len || memcmp(ap, bp, a->es->len);
-
- if (DIFF_XDL_TST(diffopt, IGNORE_WHITESPACE_AT_EOL)) {
- while (ae > ap && isspace(*ae))
- ae--;
- while (be > bp && isspace(*be))
- be--;
- }
-
- while (1) {
- int ca, cb;
- ca = next_byte(&ap, &ae, diffopt);
- cb = next_byte(&bp, &be, diffopt);
- if (ca != cb)
- return 1;
- if (ca < 0)
- return 0;
- }
-}
-
-static unsigned get_string_hash(struct emitted_diff_symbol *es, struct diff_options *o)
-{
- if (o->xdl_opts & XDF_WHITESPACE_FLAGS) {
- static struct strbuf sb = STRBUF_INIT;
- const char *ap = es->line, *ae = es->line + es->len;
- int c;
-
- strbuf_reset(&sb);
- while (ae > ap && isspace(*ae))
- ae--;
- while ((c = next_byte(&ap, &ae, o)) > 0)
- strbuf_addch(&sb, c);
-
- return memhash(sb.buf, sb.len);
- } else {
- return memhash(es->line, es->len);
- }
+ return !xdiff_compare_lines(a->es->line, a->es->len,
+ b->es->line, b->es->len,
+ diffopt->xdl_opts);
}
static struct moved_entry *prepare_entry(struct diff_options *o,
struct moved_entry *ret = xmalloc(sizeof(*ret));
struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
- ret->ent.hash = get_string_hash(l, o);
+ ret->ent.hash = xdiff_hash_string(l->line, l->len, o->xdl_opts);
ret->es = l;
ret->next_line = NULL;
struct emit_callback ecbdata;
struct strbuf out = STRBUF_INIT;
- if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
+ if (diff_mnemonic_prefix && o->flags.reverse_diff) {
a_prefix = o->b_prefix;
b_prefix = o->a_prefix;
} else {
dir.alloc = 0;
dir.nr = 0;
dir.permille = options->dirstat_permille;
- dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);
+ dir.cumulative = options->flags.dirstat_cumulative;
changed = 0;
for (i = 0; i < q->nr; i++) {
goto found_damage;
}
- if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE)) {
+ if (options->flags.dirstat_by_file) {
/*
* In --dirstat-by-file mode, we don't really need to
* look at the actual file contents at all.
dir.alloc = 0;
dir.nr = 0;
dir.permille = options->dirstat_permille;
- dir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);
+ dir.cumulative = options->flags.dirstat_cumulative;
changed = 0;
for (i = 0; i < data->nr; i++) {
const char *line_prefix = diff_line_prefix(o);
diff_set_mnemonic_prefix(o, "a/", "b/");
- if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+ if (o->flags.reverse_diff) {
a_prefix = o->b_prefix;
b_prefix = o->a_prefix;
} else {
return;
}
- if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
+ if (o->flags.allow_textconv) {
textconv_one = get_textconv(one);
textconv_two = get_textconv(two);
}
header.len, 0);
strbuf_reset(&header);
goto free_ab_and_return;
- } else if (!DIFF_OPT_TST(o, TEXT) &&
+ } else if (!o->flags.text &&
( (!textconv_one && diff_filespec_is_binary(one)) ||
(!textconv_two && diff_filespec_is_binary(two)) )) {
struct strbuf sb = STRBUF_INIT;
if (!one->data && !two->data &&
S_ISREG(one->mode) && S_ISREG(two->mode) &&
- !DIFF_OPT_TST(o, BINARY)) {
+ !o->flags.binary) {
if (!oidcmp(&one->oid, &two->oid)) {
if (must_show_header)
emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
}
emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf, header.len, 0);
strbuf_reset(&header);
- if (DIFF_OPT_TST(o, BINARY))
+ if (o->flags.binary)
emit_binary_diff(o, &mf1, &mf2);
else {
strbuf_addf(&sb, "%sBinary files %s and %s differ\n",
xecfg.ctxlen = o->context;
xecfg.interhunkctxlen = o->interhunkcontext;
xecfg.flags = XDL_EMIT_FUNCNAMES;
- if (DIFF_OPT_TST(o, FUNCCONTEXT))
+ if (o->flags.funccontext)
xecfg.flags |= XDL_EMIT_FUNCCONTEXT;
if (pe)
xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
diff_free_filespec_data(one);
diff_free_filespec_data(two);
if (data.status)
- DIFF_OPT_SET(o, CHECK_FAILED);
+ o->flags.check_failed = 1;
}
struct diff_filespec *alloc_filespec(const char *path)
int fd;
if (lstat(s->path, &st) < 0) {
- if (errno == ENOENT) {
- err_empty:
- err = -1;
- empty:
- s->data = (char *)"";
- s->size = 0;
- return err;
- }
+ err_empty:
+ err = -1;
+ empty:
+ s->data = (char *)"";
+ s->size = 0;
+ return err;
}
s->size = xsize_t(st.st_size);
if (!s->size)
*must_show_header = 0;
}
if (one && two && oidcmp(&one->oid, &two->oid)) {
- int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
+ int abbrev = o->flags.full_index ? 40 : DEFAULT_ABBREV;
- if (DIFF_OPT_TST(o, BINARY)) {
+ if (o->flags.binary) {
mmfile_t mf;
if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
(!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
int must_show_header = 0;
- if (DIFF_OPT_TST(o, ALLOW_EXTERNAL)) {
+ if (o->flags.allow_external) {
struct userdiff_driver *drv = userdiff_find_by_path(attr_path);
if (drv && drv->external)
pgm = drv->external;
if (o->prefix_length)
strip_prefix(o->prefix_length, &name, &other);
- if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
+ if (!o->flags.allow_external)
pgm = NULL;
if (DIFF_PAIR_UNMERGED(p)) {
options->context = diff_context_default;
options->interhunkcontext = diff_interhunk_context_default;
options->ws_error_highlight = ws_error_highlight_default;
- DIFF_OPT_SET(options, RENAME_EMPTY);
+ options->flags.rename_empty = 1;
/* pathchange left =NULL by default */
options->change = diff_change;
if (DIFF_XDL_TST(options, IGNORE_WHITESPACE) ||
DIFF_XDL_TST(options, IGNORE_WHITESPACE_CHANGE) ||
DIFF_XDL_TST(options, IGNORE_WHITESPACE_AT_EOL))
- DIFF_OPT_SET(options, DIFF_FROM_CONTENTS);
+ options->flags.diff_from_contents = 1;
else
- DIFF_OPT_CLR(options, DIFF_FROM_CONTENTS);
+ options->flags.diff_from_contents = 0;
- if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
+ if (options->flags.find_copies_harder)
options->detect_rename = DIFF_DETECT_COPY;
- if (!DIFF_OPT_TST(options, RELATIVE_NAME))
+ if (!options->flags.relative_name)
options->prefix = NULL;
if (options->prefix)
options->prefix_length = strlen(options->prefix);
DIFF_FORMAT_DIRSTAT |
DIFF_FORMAT_SUMMARY |
DIFF_FORMAT_CHECKDIFF))
- DIFF_OPT_SET(options, RECURSIVE);
+ options->flags.recursive = 1;
/*
* Also pickaxe would not work very well if you do not say recursive
*/
if (options->pickaxe)
- DIFF_OPT_SET(options, RECURSIVE);
+ options->flags.recursive = 1;
/*
* When patches are generated, submodules diffed against the work tree
* must be checked for dirtiness too so it can be shown in the output
*/
if (options->output_format & DIFF_FORMAT_PATCH)
- DIFF_OPT_SET(options, DIRTY_SUBMODULES);
+ options->flags.dirty_submodules = 1;
if (options->detect_rename && options->rename_limit < 0)
options->rename_limit = diff_rename_limit_default;
* to have found. It does not make sense not to return with
* exit code in such a case either.
*/
- if (DIFF_OPT_TST(options, QUICK)) {
+ if (options->flags.quick) {
options->output_format = DIFF_FORMAT_NO_OUTPUT;
- DIFF_OPT_SET(options, EXIT_WITH_STATUS);
+ options->flags.exit_with_status = 1;
}
options->diff_path_counter = 0;
- if (DIFF_OPT_TST(options, FOLLOW_RENAMES) && options->pathspec.nr != 1)
+ if (options->flags.follow_renames && options->pathspec.nr != 1)
die(_("--follow requires exactly one pathspec"));
if (!options->use_color || external_diff())
else if (starts_with(arg, "-C") || starts_with(arg, "--find-copies=") ||
!strcmp(arg, "--find-copies")) {
if (options->detect_rename == DIFF_DETECT_COPY)
- DIFF_OPT_SET(options, FIND_COPIES_HARDER);
+ options->flags.find_copies_harder = 1;
if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
return error("invalid argument to -C: %s", arg+2);
options->detect_rename = DIFF_DETECT_COPY;
else if (!strcmp(arg, "--no-renames"))
options->detect_rename = 0;
else if (!strcmp(arg, "--rename-empty"))
- DIFF_OPT_SET(options, RENAME_EMPTY);
+ options->flags.rename_empty = 1;
else if (!strcmp(arg, "--no-rename-empty"))
- DIFF_OPT_CLR(options, RENAME_EMPTY);
+ options->flags.rename_empty = 0;
else if (!strcmp(arg, "--relative"))
- DIFF_OPT_SET(options, RELATIVE_NAME);
+ options->flags.relative_name = 1;
else if (skip_prefix(arg, "--relative=", &arg)) {
- DIFF_OPT_SET(options, RELATIVE_NAME);
+ options->flags.relative_name = 1;
options->prefix = arg;
}
/* flags options */
else if (!strcmp(arg, "--binary")) {
enable_patch_output(&options->output_format);
- DIFF_OPT_SET(options, BINARY);
+ options->flags.binary = 1;
}
else if (!strcmp(arg, "--full-index"))
- DIFF_OPT_SET(options, FULL_INDEX);
+ options->flags.full_index = 1;
else if (!strcmp(arg, "-a") || !strcmp(arg, "--text"))
- DIFF_OPT_SET(options, TEXT);
+ options->flags.text = 1;
else if (!strcmp(arg, "-R"))
- DIFF_OPT_SET(options, REVERSE_DIFF);
+ options->flags.reverse_diff = 1;
else if (!strcmp(arg, "--find-copies-harder"))
- DIFF_OPT_SET(options, FIND_COPIES_HARDER);
+ options->flags.find_copies_harder = 1;
else if (!strcmp(arg, "--follow"))
- DIFF_OPT_SET(options, FOLLOW_RENAMES);
+ options->flags.follow_renames = 1;
else if (!strcmp(arg, "--no-follow")) {
- DIFF_OPT_CLR(options, FOLLOW_RENAMES);
- DIFF_OPT_CLR(options, DEFAULT_FOLLOW_RENAMES);
+ options->flags.follow_renames = 0;
+ options->flags.default_follow_renames = 0;
} else if (!strcmp(arg, "--color"))
options->use_color = 1;
else if (skip_prefix(arg, "--color=", &arg)) {
return argcount;
}
else if (!strcmp(arg, "--exit-code"))
- DIFF_OPT_SET(options, EXIT_WITH_STATUS);
+ options->flags.exit_with_status = 1;
else if (!strcmp(arg, "--quiet"))
- DIFF_OPT_SET(options, QUICK);
+ options->flags.quick = 1;
else if (!strcmp(arg, "--ext-diff"))
- DIFF_OPT_SET(options, ALLOW_EXTERNAL);
+ options->flags.allow_external = 1;
else if (!strcmp(arg, "--no-ext-diff"))
- DIFF_OPT_CLR(options, ALLOW_EXTERNAL);
- else if (!strcmp(arg, "--textconv"))
- DIFF_OPT_SET(options, ALLOW_TEXTCONV);
- else if (!strcmp(arg, "--no-textconv"))
- DIFF_OPT_CLR(options, ALLOW_TEXTCONV);
+ options->flags.allow_external = 0;
+ else if (!strcmp(arg, "--textconv")) {
+ options->flags.allow_textconv = 1;
+ options->flags.textconv_set_via_cmdline = 1;
+ } else if (!strcmp(arg, "--no-textconv"))
+ options->flags.allow_textconv = 0;
else if (!strcmp(arg, "--ignore-submodules")) {
- DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
+ options->flags.override_submodule_config = 1;
handle_ignore_submodules_arg(options, "all");
} else if (skip_prefix(arg, "--ignore-submodules=", &arg)) {
- DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
+ options->flags.override_submodule_config = 1;
handle_ignore_submodules_arg(options, arg);
} else if (!strcmp(arg, "--submodule"))
options->submodule_format = DIFF_SUBMODULE_LOG;
&options->interhunkcontext))
;
else if (!strcmp(arg, "-W"))
- DIFF_OPT_SET(options, FUNCCONTEXT);
+ options->flags.funccontext = 1;
else if (!strcmp(arg, "--function-context"))
- DIFF_OPT_SET(options, FUNCCONTEXT);
+ options->flags.funccontext = 1;
else if (!strcmp(arg, "--no-function-context"))
- DIFF_OPT_CLR(options, FUNCCONTEXT);
+ options->flags.funccontext = 0;
else if ((argcount = parse_long_opt("output", av, &optarg))) {
char *path = prefix_filename(prefix, optarg);
options->file = xfopen(path, "w");
separator++;
}
- if (output_format & DIFF_FORMAT_DIRSTAT && DIFF_OPT_TST(options, DIRSTAT_BY_LINE))
+ if (output_format & DIFF_FORMAT_DIRSTAT && options->flags.dirstat_by_line)
dirstat_by_line = 1;
if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) ||
}
if (output_format & DIFF_FORMAT_NO_OUTPUT &&
- DIFF_OPT_TST(options, EXIT_WITH_STATUS) &&
- DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
+ options->flags.exit_with_status &&
+ options->flags.diff_from_contents) {
/*
* run diff_flush_patch for the exit status. setting
* options->file to /dev/null should be safe, because we
* diff_addremove/diff_change does not set the bit when
* DIFF_FROM_CONTENTS is in effect (e.g. with -w).
*/
- if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
+ if (options->flags.diff_from_contents) {
if (options->found_changes)
- DIFF_OPT_SET(options, HAS_CHANGES);
+ options->flags.has_changes = 1;
else
- DIFF_OPT_CLR(options, HAS_CHANGES);
+ options->flags.has_changes = 0;
}
}
* to determine how many paths were dirty only
* due to stat info mismatch.
*/
- if (!DIFF_OPT_TST(diffopt, NO_INDEX))
+ if (!diffopt->flags.no_index)
diffopt->skip_stat_unmatch++;
diff_free_filepair(p);
}
diff_resolve_rename_copy();
diffcore_apply_filter(options);
- if (diff_queued_diff.nr && !DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
- DIFF_OPT_SET(options, HAS_CHANGES);
+ if (diff_queued_diff.nr && !options->flags.diff_from_contents)
+ options->flags.has_changes = 1;
else
- DIFF_OPT_CLR(options, HAS_CHANGES);
+ options->flags.has_changes = 0;
options->found_follow = 0;
}
diff_warn_rename_limit("diff.renameLimit",
opt->needed_rename_limit,
opt->degraded_cc_to_c);
- if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
+ if (!opt->flags.exit_with_status &&
!(opt->output_format & DIFF_FORMAT_CHECKDIFF))
return status;
- if (DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
- DIFF_OPT_TST(opt, HAS_CHANGES))
+ if (opt->flags.exit_with_status &&
+ opt->flags.has_changes)
result |= 01;
if ((opt->output_format & DIFF_FORMAT_CHECKDIFF) &&
- DIFF_OPT_TST(opt, CHECK_FAILED))
+ opt->flags.check_failed)
result |= 02;
return result;
}
int diff_can_quit_early(struct diff_options *opt)
{
- return (DIFF_OPT_TST(opt, QUICK) &&
+ return (opt->flags.quick &&
!opt->filter &&
- DIFF_OPT_TST(opt, HAS_CHANGES));
+ opt->flags.has_changes);
}
/*
static int is_submodule_ignored(const char *path, struct diff_options *options)
{
int ignored = 0;
- unsigned orig_flags = options->flags;
- if (!DIFF_OPT_TST(options, OVERRIDE_SUBMODULE_CONFIG))
+ struct diff_flags orig_flags = options->flags;
+ if (!options->flags.override_submodule_config)
set_diffopt_flags_from_submodule_config(options, path);
- if (DIFF_OPT_TST(options, IGNORE_SUBMODULES))
+ if (options->flags.ignore_submodules)
ignored = 1;
options->flags = orig_flags;
return ignored;
* Before the final output happens, they are pruned after
* merged into rename/copy pairs as appropriate.
*/
- if (DIFF_OPT_TST(options, REVERSE_DIFF))
+ if (options->flags.reverse_diff)
addremove = (addremove == '+' ? '-' :
addremove == '-' ? '+' : addremove);
}
diff_queue(&diff_queued_diff, one, two);
- if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
- DIFF_OPT_SET(options, HAS_CHANGES);
+ if (!options->flags.diff_from_contents)
+ options->flags.has_changes = 1;
}
void diff_change(struct diff_options *options,
is_submodule_ignored(concatpath, options))
return;
- if (DIFF_OPT_TST(options, REVERSE_DIFF)) {
+ if (options->flags.reverse_diff) {
SWAP(old_mode, new_mode);
SWAP(old_oid, new_oid);
SWAP(old_oid_valid, new_oid_valid);
two->dirty_submodule = new_dirty_submodule;
p = diff_queue(&diff_queued_diff, one, two);
- if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
+ if (options->flags.diff_from_contents)
return;
- if (DIFF_OPT_TST(options, QUICK) && options->skip_stat_unmatch &&
+ if (options->flags.quick && options->skip_stat_unmatch &&
!diff_filespec_check_stat_unmatch(p))
return;
- DIFF_OPT_SET(options, HAS_CHANGES);
+ options->flags.has_changes = 1;
}
struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path)
* and because it is easy to find people oneline advising "git diff
* --exit-code" in hooks and other scripts, we do not do so.
*/
- if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
+ if (!opt->flags.exit_with_status &&
check_pager_config("diff") != 0)
setup_pager();
}
#define DIFF_FORMAT_CALLBACK 0x1000
-#define DIFF_OPT_RECURSIVE (1 << 0)
-#define DIFF_OPT_TREE_IN_RECURSIVE (1 << 1)
-#define DIFF_OPT_BINARY (1 << 2)
-#define DIFF_OPT_TEXT (1 << 3)
-#define DIFF_OPT_FULL_INDEX (1 << 4)
-#define DIFF_OPT_SILENT_ON_REMOVE (1 << 5)
-#define DIFF_OPT_FIND_COPIES_HARDER (1 << 6)
-#define DIFF_OPT_FOLLOW_RENAMES (1 << 7)
-#define DIFF_OPT_RENAME_EMPTY (1 << 8)
-/* (1 << 9) unused */
-#define DIFF_OPT_HAS_CHANGES (1 << 10)
-#define DIFF_OPT_QUICK (1 << 11)
-#define DIFF_OPT_NO_INDEX (1 << 12)
-#define DIFF_OPT_ALLOW_EXTERNAL (1 << 13)
-#define DIFF_OPT_EXIT_WITH_STATUS (1 << 14)
-#define DIFF_OPT_REVERSE_DIFF (1 << 15)
-#define DIFF_OPT_CHECK_FAILED (1 << 16)
-#define DIFF_OPT_RELATIVE_NAME (1 << 17)
-#define DIFF_OPT_IGNORE_SUBMODULES (1 << 18)
-#define DIFF_OPT_DIRSTAT_CUMULATIVE (1 << 19)
-#define DIFF_OPT_DIRSTAT_BY_FILE (1 << 20)
-#define DIFF_OPT_ALLOW_TEXTCONV (1 << 21)
-#define DIFF_OPT_DIFF_FROM_CONTENTS (1 << 22)
-#define DIFF_OPT_DIRTY_SUBMODULES (1 << 24)
-#define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)
-#define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
-#define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
-#define DIFF_OPT_DIRSTAT_BY_LINE (1 << 28)
-#define DIFF_OPT_FUNCCONTEXT (1 << 29)
-#define DIFF_OPT_PICKAXE_IGNORE_CASE (1 << 30)
-#define DIFF_OPT_DEFAULT_FOLLOW_RENAMES (1U << 31)
-
-#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
-#define DIFF_OPT_TOUCHED(opts, flag) ((opts)->touched_flags & DIFF_OPT_##flag)
-#define DIFF_OPT_SET(opts, flag) (((opts)->flags |= DIFF_OPT_##flag),((opts)->touched_flags |= DIFF_OPT_##flag))
-#define DIFF_OPT_CLR(opts, flag) (((opts)->flags &= ~DIFF_OPT_##flag),((opts)->touched_flags |= DIFF_OPT_##flag))
+#define DIFF_FLAGS_INIT { 0 }
+struct diff_flags {
+ unsigned recursive:1;
+ unsigned tree_in_recursive:1;
+ unsigned binary:1;
+ unsigned text:1;
+ unsigned full_index:1;
+ unsigned silent_on_remove:1;
+ unsigned find_copies_harder:1;
+ unsigned follow_renames:1;
+ unsigned rename_empty:1;
+ unsigned has_changes:1;
+ unsigned quick:1;
+ unsigned no_index:1;
+ unsigned allow_external:1;
+ unsigned exit_with_status:1;
+ unsigned reverse_diff:1;
+ unsigned check_failed:1;
+ unsigned relative_name:1;
+ unsigned ignore_submodules:1;
+ unsigned dirstat_cumulative:1;
+ unsigned dirstat_by_file:1;
+ unsigned allow_textconv:1;
+ unsigned textconv_set_via_cmdline:1;
+ unsigned diff_from_contents:1;
+ unsigned dirty_submodules:1;
+ unsigned ignore_untracked_in_submodules:1;
+ unsigned ignore_dirty_submodules:1;
+ unsigned override_submodule_config:1;
+ unsigned dirstat_by_line:1;
+ unsigned funccontext:1;
+ unsigned pickaxe_ignore_case:1;
+ unsigned default_follow_renames:1;
+};
+
+static inline void diff_flags_or(struct diff_flags *a,
+ const struct diff_flags *b)
+{
+ char *tmp_a = (char *)a;
+ const char *tmp_b = (const char *)b;
+ int i;
+
+ for (i = 0; i < sizeof(struct diff_flags); i++)
+ tmp_a[i] |= tmp_b[i];
+}
+
#define DIFF_XDL_TST(opts, flag) ((opts)->xdl_opts & XDF_##flag)
#define DIFF_XDL_SET(opts, flag) ((opts)->xdl_opts |= XDF_##flag)
#define DIFF_XDL_CLR(opts, flag) ((opts)->xdl_opts &= ~XDF_##flag)
const char *a_prefix, *b_prefix;
const char *line_prefix;
size_t line_prefix_length;
- unsigned flags;
- unsigned touched_flags;
+ struct diff_flags flags;
/* diff-filter bits */
unsigned int filter;
pathchange_fn_t pathchange;
change_fn_t change;
add_remove_fn_t add_remove;
+ void *change_fn_data;
diff_format_fn_t format_callback;
void *format_callback_data;
diff_prefix_fn_t output_prefix;
extern void diff_no_index(struct rev_info *, int, const char **);
-extern int index_differs_from(const char *def, int diff_flags, int ita_invisible_in_index);
+extern int index_differs_from(const char *def, const struct diff_flags *flags,
+ int ita_invisible_in_index);
/*
* Fill the contents of the filespec "df", respecting any textconv defined by
if (!DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two))
return 0;
- if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
+ if (o->flags.allow_textconv) {
textconv_one = get_textconv(p->one);
textconv_two = get_textconv(p->two);
}
if (opts & (DIFF_PICKAXE_REGEX | DIFF_PICKAXE_KIND_G)) {
int cflags = REG_EXTENDED | REG_NEWLINE;
- if (DIFF_OPT_TST(o, PICKAXE_IGNORE_CASE))
+ if (o->flags.pickaxe_ignore_case)
cflags |= REG_ICASE;
regcomp_or_die(®ex, needle, cflags);
regexp = ®ex;
- } else if (DIFF_OPT_TST(o, PICKAXE_IGNORE_CASE) &&
+ } else if (o->flags.pickaxe_ignore_case &&
has_non_ascii(needle)) {
struct strbuf sb = STRBUF_INIT;
int cflags = REG_NEWLINE | REG_ICASE;
strbuf_release(&sb);
regexp = ®ex;
} else {
- kws = kwsalloc(DIFF_OPT_TST(o, PICKAXE_IGNORE_CASE)
+ kws = kwsalloc(o->flags.pickaxe_ignore_case
? tolower_trans_tbl : NULL);
kwsincr(kws, needle, strlen(needle));
kwsprep(kws);
num_src > num_create ? num_src : num_create;
/* Are we running under -C -C? */
- if (!DIFF_OPT_TST(options, FIND_COPIES_HARDER))
+ if (!options->flags.find_copies_harder)
return 1;
/* Would we bust the limit if we were running under -C? */
else if (options->single_follow &&
strcmp(options->single_follow, p->two->path))
continue; /* not interested */
- else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
+ else if (!options->flags.rename_empty &&
is_empty_blob_oid(&p->two->oid))
continue;
else if (add_rename_dst(p->two) < 0) {
goto cleanup;
}
}
- else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
+ else if (!options->flags.rename_empty &&
is_empty_blob_oid(&p->one->oid))
continue;
else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) {
case index_nonexistent:
if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
break;
+ if (exclude &&
+ (dir->flags & DIR_SHOW_IGNORED_TOO) &&
+ (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING)) {
+
+ /*
+ * This is an excluded directory and we are
+ * showing ignored paths that match an exclude
+ * pattern. (e.g. show directory as ignored
+ * only if it matches an exclude pattern).
+ * This path will either be 'path_excluded`
+ * (if we are showing empty directories or if
+ * the directory is not empty), or will be
+ * 'path_none' (empty directory, and we are
+ * not showing empty directories).
+ */
+ if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
+ return path_excluded;
+
+ if (read_directory_recursive(dir, istate, dirname, len,
+ untracked, 1, 1, pathspec) == path_excluded)
+ return path_excluded;
+
+ return path_none;
+ }
if (!(dir->flags & DIR_NO_GITLINKS)) {
struct object_id oid;
if (resolve_gitlink_ref(dirname, "HEAD", &oid) == 0)
- return path_untracked;
+ return exclude ? path_excluded : path_untracked;
}
return path_recurse;
}
{
int exclude;
int has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case);
+ enum path_treatment path_treatment;
if (dtype == DT_UNKNOWN)
dtype = get_dtype(de, istate, path->buf, path->len);
return path_none;
case DT_DIR:
strbuf_addch(path, '/');
- return treat_directory(dir, istate, untracked, path->buf, path->len,
- baselen, exclude, pathspec);
+ path_treatment = treat_directory(dir, istate, untracked,
+ path->buf, path->len,
+ baselen, exclude, pathspec);
+ /*
+ * If 1) we only want to return directories that
+ * match an exclude pattern and 2) this directory does
+ * not match an exclude pattern but all of its
+ * contents are excluded, then indicate that we should
+ * recurse into this directory (instead of marking the
+ * directory itself as an ignored path).
+ */
+ if (!exclude &&
+ path_treatment == path_excluded &&
+ (dir->flags & DIR_SHOW_IGNORED_TOO) &&
+ (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING))
+ return path_recurse;
+ return path_treatment;
case DT_REG:
case DT_LNK:
return exclude ? path_excluded : path_untracked;
DIR_COLLECT_IGNORED = 1<<4,
DIR_SHOW_IGNORED_TOO = 1<<5,
DIR_COLLECT_KILLED_ONLY = 1<<6,
- DIR_KEEP_UNTRACKED_CONTENTS = 1<<7
+ DIR_KEEP_UNTRACKED_CONTENTS = 1<<7,
+ DIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8
} flags;
struct dir_entry **entries;
struct dir_entry **ignored;
#
# Get rid of any old bisect state.
#
- bisect_clean_state || exit
+ git bisect--helper --bisect-clean-state || exit
#
# Change state.
# We have to trap this to be able to clean up using
# "bisect_clean_state".
#
- trap 'bisect_clean_state' 0
+ trap 'git bisect--helper --bisect-clean-state' 0
trap 'exit 255' 1 2 3 15
#
eval "$eval true" &&
if test $must_write_terms -eq 1
then
- write_terms "$TERM_BAD" "$TERM_GOOD"
+ git bisect--helper --write-terms "$TERM_BAD" "$TERM_GOOD" || exit
fi &&
echo "git bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" || exit
#
test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
}
-is_expected_rev() {
- test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
- test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
-}
-
-check_expected_revs() {
- for _rev in "$@"; do
- if ! is_expected_rev "$_rev"
- then
- rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
- rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
- return
- fi
- done
-}
-
bisect_skip() {
all=''
for arg in "$@"
rev=$(git rev-parse --verify "$bisected_head") ||
die "$(eval_gettext "Bad rev input: \$bisected_head")"
bisect_write "$state" "$rev"
- check_expected_revs "$rev" ;;
+ git bisect--helper --check-expected-revs "$rev" ;;
2,"$TERM_BAD"|*,"$TERM_GOOD"|*,skip)
shift
hash_list=''
do
bisect_write "$state" "$rev"
done
- check_expected_revs $hash_list ;;
+ git bisect--helper --check-expected-revs $hash_list ;;
*,"$TERM_BAD")
die "$(eval_gettext "'git bisect \$TERM_BAD' can take only one argument.")" ;;
*)
die "$(eval_gettext "Could not check out original HEAD '\$branch'.
Try 'git bisect reset <commit>'.")"
fi
- bisect_clean_state
-}
-
-bisect_clean_state() {
- # There may be some refs packed during bisection.
- git for-each-ref --format='%(refname) %(objectname)' refs/bisect/\* |
- while read ref hash
- do
- git update-ref -d $ref $hash || exit
- done
- rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
- rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
- rm -f "$GIT_DIR/BISECT_LOG" &&
- rm -f "$GIT_DIR/BISECT_NAMES" &&
- rm -f "$GIT_DIR/BISECT_RUN" &&
- rm -f "$GIT_DIR/BISECT_TERMS" &&
- # Cleanup head-name if it got left by an old version of git-bisect
- rm -f "$GIT_DIR/head-name" &&
- git update-ref -d --no-deref BISECT_HEAD &&
- # clean up BISECT_START last
- rm -f "$GIT_DIR/BISECT_START"
+ git bisect--helper --bisect-clean-state || exit
}
bisect_replay () {
fi
}
-write_terms () {
- TERM_BAD=$1
- TERM_GOOD=$2
- if test "$TERM_BAD" = "$TERM_GOOD"
- then
- die "$(gettext "please use two different terms")"
- fi
- check_term_format "$TERM_BAD" bad
- check_term_format "$TERM_GOOD" good
- printf '%s\n%s\n' "$TERM_BAD" "$TERM_GOOD" >"$GIT_DIR/BISECT_TERMS"
-}
-
-check_term_format () {
- term=$1
- git check-ref-format refs/bisect/"$term" ||
- die "$(eval_gettext "'\$term' is not a valid term")"
- case "$term" in
- help|start|terms|skip|next|reset|visualize|replay|log|run)
- die "$(eval_gettext "can't use the builtin command '\$term' as a term")"
- ;;
- bad|new)
- if test "$2" != bad
- then
- # In theory, nothing prevents swapping
- # completely good and bad, but this situation
- # could be confusing and hasn't been tested
- # enough. Forbid it for now.
- die "$(eval_gettext "can't change the meaning of term '\$term'")"
- fi
- ;;
- good|old)
- if test "$2" != good
- then
- die "$(eval_gettext "can't change the meaning of term '\$term'")"
- fi
- ;;
- esac
-}
-
check_and_set_terms () {
cmd="$1"
case "$cmd" in
bad|good)
if ! test -s "$GIT_DIR/BISECT_TERMS"
then
- write_terms bad good
+ TERM_BAD=bad
+ TERM_GOOD=good
+ git bisect--helper --write-terms "$TERM_BAD" "$TERM_GOOD" || exit
fi
;;
new|old)
if ! test -s "$GIT_DIR/BISECT_TERMS"
then
- write_terms new old
+ TERM_BAD=new
+ TERM_GOOD=old
+ git bisect--helper --write-terms "$TERM_BAD" "$TERM_GOOD" || exit
fi
;;
esac ;;
;;
-*)
option="$1"
- # TRANSLATORS: $option is an invalid option, like
- # `--blah-blah'. The 7 spaces at the beginning of the
- # second line correspond to "error: ". So you should line
- # up the second line with however many characters the
- # translation of "error: " takes in your language. E.g. in
- # English this is:
- #
- # $ git stash save --blah-blah 2>&1 | head -n 2
- # error: unknown option for 'stash save': --blah-blah
- # To provide a message, use git stash save -- '--blah-blah'
- eval_gettextln "error: unknown option for 'stash save': \$option
- To provide a message, use git stash save -- '\$option'"
+ eval_gettextln "error: unknown option for 'stash push': \$option"
usage
;;
*)
}
}
-set_name_rev () {
- revname=$( (
- sanitize_submodule_env
- cd "$1" && {
- git describe "$2" 2>/dev/null ||
- git describe --tags "$2" 2>/dev/null ||
- git describe --contains "$2" 2>/dev/null ||
- git describe --all --always "$2"
- }
- ) )
- test -z "$revname" || revname=" ($revname)"
-}
#
# Show commit summary for submodules in index or working tree
#
shift
done
- {
- git submodule--helper list --prefix "$wt_prefix" "$@" ||
- echo "#unmatched" $?
- } |
- while read -r mode sha1 stage sm_path
- do
- die_if_unmatched "$mode" "$sha1"
- name=$(git submodule--helper name "$sm_path") || exit
- displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
- if test "$stage" = U
- then
- say "U$sha1 $displaypath"
- continue
- fi
- if ! git submodule--helper is-active "$sm_path" ||
- {
- ! test -d "$sm_path"/.git &&
- ! test -f "$sm_path"/.git
- }
- then
- say "-$sha1 $displaypath"
- continue;
- fi
- if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path"
- then
- set_name_rev "$sm_path" "$sha1"
- say " $sha1 $displaypath$revname"
- else
- if test -z "$cached"
- then
- sha1=$(sanitize_submodule_env; cd "$sm_path" && git rev-parse --verify HEAD)
- fi
- set_name_rev "$sm_path" "$sha1"
- say "+$sha1 $displaypath$revname"
- fi
-
- if test -n "$recursive"
- then
- (
- prefix="$displaypath/"
- sanitize_submodule_env
- wt_prefix=
- cd "$sm_path" &&
- eval cmd_status
- ) ||
- die "$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")"
- fi
- done
+ git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper status ${GIT_QUIET:+--quiet} ${cached:+--cached} ${recursive:+--recursive} "$@"
}
#
# Sync remote urls for submodules
1 VERSIONINFO
-FILEVERSION MAJOR,MINOR,0,0
-PRODUCTVERSION MAJOR,MINOR,0,0
+FILEVERSION MAJOR,MINOR,MICRO,PATCHLEVEL
+PRODUCTVERSION MAJOR,MINOR,MICRO,PATCHLEVEL
BEGIN
BLOCK "StringFileInfo"
BEGIN
use 5.008;
use strict;
use warnings;
+# handle ACL in file access tests
+use filetest 'access';
use CGI qw(:standard :escapeHTML -nosticky);
use CGI::Util qw(unescape);
use CGI::Carp qw(fatalsToBrowser set_message);
-1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */
};
+int hex_to_bytes(unsigned char *binary, const char *hex, size_t len)
+{
+ for (; len; len--, hex += 2) {
+ unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
+
+ if (val & ~0xff)
+ return -1;
+ *binary++ = val;
+ }
+ return 0;
+}
+
int get_sha1_hex(const char *hex, unsigned char *sha1)
{
int i;
void (*userFunc)(struct remote_ls_ctx *ls),
void *userData);
-/* extract hex from sharded "xx/x{40}" filename */
+/* extract hex from sharded "xx/x{38}" filename */
static int get_oid_hex_from_objpath(const char *path, struct object_id *oid)
{
- char hex[GIT_MAX_HEXSZ];
-
if (strlen(path) != GIT_SHA1_HEXSZ + 1)
return -1;
- memcpy(hex, path, 2);
+ if (hex_to_bytes(oid->hash, path, 1))
+ return -1;
path += 2;
path++; /* skip '/' */
- memcpy(hex + 2, path, GIT_SHA1_HEXSZ - 2);
- return get_oid_hex(hex, oid);
+ return hex_to_bytes(oid->hash + 1, path, GIT_SHA1_RAWSZ - 1);
}
static void process_ls_object(struct remote_ls_ctx *ls)
struct imap *imap = ctx->imap;
char *arg, *p;
- if (*s != '[')
+ if (!s || *s != '[')
return RESP_OK; /* no response code */
s++;
if (!(p = strchr(s, ']'))) {
}
*p++ = 0;
arg = next_arg(&s);
+ if (!arg) {
+ fprintf(stderr, "IMAP error: empty response code\n");
+ return RESP_BAD;
+ }
if (!strcmp("UIDVALIDITY", arg)) {
if (!(arg = next_arg(&s)) || !(ctx->uidvalidity = atoi(arg))) {
fprintf(stderr, "IMAP error: malformed UIDVALIDITY status\n");
{
struct imap *imap = ctx->imap;
struct imap_cmd *cmdp, **pcmdp;
- char *cmd, *arg, *arg1;
+ char *cmd;
+ const char *arg, *arg1;
int n, resp, resp2, tag;
for (;;) {
return RESP_BAD;
arg = next_arg(&cmd);
+ if (!arg) {
+ fprintf(stderr, "IMAP error: empty response\n");
+ return RESP_BAD;
+ }
if (*arg == '*') {
arg = next_arg(&cmd);
if (!arg) {
if (cmdp->cb.cont || cmdp->cb.data)
imap->literal_pending = 0;
arg = next_arg(&cmd);
+ if (!arg)
+ arg = "";
if (!strcmp("OK", arg))
resp = DRV_OK;
else {
* If the lockfile is still open, close it (and the file pointer if it
* has been opened using `fdopen_lock_file()`) without renaming the
* lockfile over the file being locked. Return 0 upon success. On
- * failure to `close(2)`, return a negative value and roll back the
- * lock file. Usually `commit_lock_file()`, `commit_lock_file_to()`,
+ * failure to `close(2)`, return a negative value (the lockfile is not
+ * rolled back). Usually `commit_lock_file()`, `commit_lock_file_to()`,
* or `rollback_lock_file()` should eventually be called.
*/
static inline int close_lock_file_gently(struct lock_file *lk)
/* Now resolve and find the matching current branch */
branch_name = resolve_ref_unsafe("HEAD", 0, NULL, &rru_flags);
- if (!(rru_flags & REF_ISSYMREF))
+ if (!branch_name || !(rru_flags & REF_ISSYMREF))
return NULL;
if (!starts_with(branch_name, "refs/"))
struct commit_list *parents;
struct object_id *oid;
- if (!opt->diff && !DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS))
+ if (!opt->diff && !opt->diffopt.flags.exit_with_status)
return 0;
parse_commit_or_die(commit);
return renames;
diff_setup(&opts);
- DIFF_OPT_SET(&opts, RECURSIVE);
- DIFF_OPT_CLR(&opts, RENAME_EMPTY);
+ opts.flags.recursive = 1;
+ opts.flags.rename_empty = 0;
opts.detect_rename = DIFF_DETECT_RENAME;
opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
o->diff_rename_limit >= 0 ? o->diff_rename_limit :
struct commit **result)
{
int clean;
- struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+ struct lock_file lock = LOCK_INIT;
struct commit *head_commit = get_ref(head, o->branch1);
struct commit *next_commit = get_ref(merge, o->branch2);
struct commit_list *ca = NULL;
}
}
- hold_locked_index(lock, LOCK_DIE_ON_ERROR);
+ hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
clean = merge_recursive(o, head_commit, next_commit, ca,
result);
if (clean < 0)
return clean;
if (active_cache_changed &&
- write_locked_index(&the_index, lock, COMMIT_LOCK))
+ write_locked_index(&the_index, &lock, COMMIT_LOCK))
return err(o, _("Unable to write index."));
return clean ? 0 : 1;
void init_merge_options(struct merge_options *o)
{
+ const char *merge_verbosity;
memset(o, 0, sizeof(struct merge_options));
o->verbosity = 2;
o->buffer_output = 1;
o->renormalize = 0;
o->detect_rename = 1;
merge_recursive_config(o);
- if (getenv("GIT_MERGE_VERBOSITY"))
- o->verbosity =
- strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10);
+ merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
+ if (merge_verbosity)
+ o->verbosity = strtol(merge_verbosity, NULL, 10);
if (o->verbosity >= 5)
o->buffer_output = 0;
strbuf_init(&o->obuf, 0);
struct tree_desc t[MAX_UNPACK_TREES];
int i, nr_trees = 0;
struct dir_struct dir;
- struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+ struct lock_file lock_file = LOCK_INIT;
refresh_cache(REFRESH_QUIET);
- if (hold_locked_index(lock_file, LOCK_REPORT_ON_ERROR) < 0)
+ if (hold_locked_index(&lock_file, LOCK_REPORT_ON_ERROR) < 0)
return -1;
memset(&trees, 0, sizeof(trees));
}
if (unpack_trees(nr_trees, t, &opts))
return -1;
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) {
- rollback_lock_file(lock_file);
+ if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
return error(_("unable to write new index file"));
- }
return 0;
}
oid_to_hex(base), oid_to_hex(remote));
diff_setup(&opt);
- DIFF_OPT_SET(&opt, RECURSIVE);
+ opt.flags.recursive = 1;
opt.output_format = DIFF_FORMAT_NO_OUTPUT;
diff_setup_done(&opt);
diff_tree_oid(base, remote, "", &opt);
len, oid_to_hex(base), oid_to_hex(local));
diff_setup(&opt);
- DIFF_OPT_SET(&opt, RECURSIVE);
+ opt.flags.recursive = 1;
opt.output_format = DIFF_FORMAT_NO_OUTPUT;
diff_setup_done(&opt);
diff_tree_oid(base, local, "", &opt);
}
}
-/*
- * Read `len` pairs of hexadecimal digits from `hex` and write the
- * values to `binary` as `len` bytes. Return 0 on success, or -1 if
- * the input does not consist of hex digits).
- */
-static int hex_to_bytes(unsigned char *binary, const char *hex, size_t len)
-{
- for (; len; len--, hex += 2) {
- unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
-
- if (val & ~0xff)
- return -1;
- *binary++ = val;
- }
- return 0;
-}
-
static int non_note_cmp(const struct non_note *a, const struct non_note *b)
{
return strcmp(a->path, b->path);
memset(ids, 0, sizeof(*ids));
diff_setup(&ids->diffopts);
ids->diffopts.detect_rename = 0;
- DIFF_OPT_SET(&ids->diffopts, RECURSIVE);
+ ids->diffopts.flags.recursive = 1;
diff_setup_done(&ids->diffopts);
hashmap_init(&ids->patches, patch_id_cmp, &ids->diffopts, 256);
return 0;
{
struct pathspec_item *item;
const char *entry = argv ? *argv : NULL;
- int i, n, prefixlen, warn_empty_string, nr_exclude = 0;
+ int i, n, prefixlen, nr_exclude = 0;
memset(pathspec, 0, sizeof(*pathspec));
}
n = 0;
- warn_empty_string = 1;
while (argv[n]) {
- if (*argv[n] == '\0' && warn_empty_string) {
- warning(_("empty strings as pathspecs will be made invalid in upcoming releases. "
- "please use . instead if you meant to match all paths"));
- warn_empty_string = 0;
- }
+ if (*argv[n] == '\0')
+ die("empty string is not a valid pathspec. "
+ "please use . instead if you meant to match all paths");
n++;
}
# amend | esmenar
# broken | malmès
# delta | diferència
-# dry | simulació
# deprecated | en desús
+# dry | simulació
# fatal | fatal
+# hook | lligam
# hunk | tros
# not supported | no està admès
# repository | dipòsit
+# setting | paràmetre
# skip | ometre
# squelch | silenciar
-# setting | paràmetre
# token | testimoni
+# unset | desassignar
#
# Alguns termes que són comandes específiques del git i d'àmbit molt tècnic
# hem decidit no traduir-los per facilitar-ne la compressió a l'usuari i perquè
# stage | «stage»
# stash | «sta»
# squash | «squash»
+# trailer | «trailer»
# unstage | «unstage»
msgid ""
msgstr ""
#: apply.c:74
#, c-format
msgid "unrecognized whitespace ignore option '%s'"
-msgstr "opció d'ignora l'espai en blanc «%s» no reconeguda"
+msgstr "opció ignora l'espai en blanc «%s» no reconeguda"
#: apply.c:125
msgid "--reject and --3way cannot be used together."
#: config.c:187
msgid "relative config include conditionals must come from files"
-msgstr ""
+msgstr "els condicionals d'inclusió de configuració relatius han de venir de fitxers"
#: config.c:721
#, c-format
#: config.c:2130
#, c-format
msgid "unknown core.untrackedCache value '%s'; using 'keep' default value"
-msgstr ""
+msgstr "valor «%s» a core.untrackedCache desconegut; utilitzant el valor per defecte «keep»"
#: config.c:2156
#, c-format
msgid "splitIndex.maxPercentChange value '%d' should be between 0 and 100"
-msgstr ""
+msgstr "valor «%d» a splitIndex.maxPercentChange ha d'estar entre 0 i 100"
#: config.c:2167
#, c-format
#: config.c:2680 builtin/remote.c:776
#, c-format
msgid "could not unset '%s'"
-msgstr "no s'ha pogut desestablir «%s»"
+msgstr "no s'ha pogut desassignar «%s»"
#: connect.c:50
msgid "The remote end hung up upon initial contact"
"color moved setting must be one of 'no', 'default', 'zebra', 'dimmed_zebra', "
"'plain'"
msgstr ""
+"el paràmetre de color en moviment ha de ser «no», «default», «zebra», «dimmed_zebra» o «plain»"
#: diff.c:341
#, c-format
#: ident.c:395
#, c-format
msgid "empty ident name (for <%s>) not allowed"
-msgstr ""
+msgstr "nom d'identitat buit (per <%s>) no és permès"
#: ident.c:401
#, c-format
#: refs.c:1792
msgid "ref updates forbidden inside quarantine environment"
-msgstr ""
+msgstr "no està permès actualitzar les referències en un entorn de quarantena"
#: refs/files-backend.c:1136
#, c-format
msgstr "%%(subject) no accepta paràmetres"
#: ref-filter.c:198
-#, fuzzy, c-format
+#, c-format
msgid "unknown %%(trailers) argument: %s"
-msgstr "paràmetre de reflexió desconegut: %s"
+msgstr "paràmetre %%(trailers) desconegut: %s"
#: ref-filter.c:221
#, c-format
#: trailer.c:753
msgid "could not read from stdin"
-msgstr "no s'ha pogut llegir des d'stdin"
+msgstr "no s'ha pogut llegir des de stdin"
#: trailer.c:1008 builtin/am.c:46
#, c-format
#: builtin/add.c:279
msgid "record only the fact that the path will be added later"
-msgstr "registra només el fet de que el camí s'afegirà més tard"
+msgstr "registra només el fet que el camí s'afegirà més tard"
#: builtin/add.c:280
msgid "add changes from all tracked and untracked files"
"\n"
"See \"git help submodule\" for more information."
msgstr ""
+"Heu afegit un altre dipòsit git dins del dipòsit actual.\n"
+"Els clons de dipòsits externs no contindran els continguts de\n"
+"del dipòsit incrustat i no saben com obtenir-ho.\n"
+"Si volíeu afegir un submòdul, useu:\n"
+"\n"
+"\tgit submodule add <url> %s\n"
+"\n"
+"Si heu afegit aquest camí per error, podeu suprimir-lo de\n"
+" l'índex amb:\n"
+"\n"
+"\tgit rm --cached %s\n"
+"\n"
+"Vegeu \"git help submodule\" per a més informació."
#: builtin/add.c:333
#, c-format
#: builtin/am.c:498
#, c-format
msgid "'%s' was deleted by the applypatch-msg hook"
-msgstr "s'ha suprimit «%s» pel ganxo applypatch-msg"
+msgstr "s'ha suprimit «%s» pel lligam applypatch-msg"
#: builtin/am.c:539
#, c-format
"such.\n"
"You might run `git rm` on a file to accept \"deleted by them\" for it."
msgstr ""
+"Encara teniu camins sense fusionar a l'índex.\n"
+"Heu de fer 'git add' a cada fitxer amb conflictes resolts per marcar-los "
+"com a tal.\n"
+"Podeu executar `git rm` en un fitxer per acceptar \"suprimit per ells\" pel fitxer."
#: builtin/am.c:2029 builtin/am.c:2033 builtin/am.c:2045 builtin/reset.c:332
#: builtin/reset.c:340
#: builtin/branch.c:588
msgid "Unset the upstream info"
-msgstr "Desestableix la informació de font"
+msgstr "Desassigna la informació de font"
#: builtin/branch.c:589
msgid "use colored output"
#: builtin/branch.c:775
msgid "too many arguments to unset upstream"
-msgstr "hi ha massa arguments per a desestablir la font"
+msgstr "hi ha massa arguments per a desassignar la font"
#: builtin/branch.c:779
msgid "could not unset upstream of HEAD when it does not point to any branch."
msgstr ""
-"no s'ha pogut desestablir la font de HEAD perquè no assenyala cap branca."
+"no s'ha pogut desassignar la font de HEAD perquè no assenyala cap branca."
#: builtin/branch.c:785
#, c-format
#: builtin/check-attr.c:22 builtin/check-ignore.c:24 builtin/hash-object.c:99
msgid "read file names from stdin"
-msgstr "llegeix els noms de fitxer d'stdin"
+msgstr "llegeix els noms de fitxer de stdin"
#: builtin/check-attr.c:24 builtin/check-ignore.c:26
msgid "terminate input and output records by a NUL character"
#: builtin/check-mailmap.c:14
msgid "also read contacts from stdin"
-msgstr "també llegeix els contactes des d'stdin"
+msgstr "també llegeix els contactes des de stdin"
#: builtin/check-mailmap.c:25
#, c-format
#: builtin/checkout.c:1277
#, c-format
msgid "'%s' is not a commit and a branch '%s' cannot be created from it"
-msgstr ""
+msgstr "«%s» no és una comissió i la branca «%s» no es pot crear a partir d'aquesta comissió"
#: builtin/checkout.c:1281
#, c-format
#: builtin/clone.c:127
msgid "don't clone any tags, and make later fetches not to follow them"
-msgstr ""
+msgstr "no cloneu cap etiqueta, i feu que els «fetch» següents no els segueixen"
#: builtin/clone.c:129
msgid "any cloned submodules will be shallow"
#: builtin/commit.c:1624
msgid "bypass pre-commit and commit-msg hooks"
-msgstr "evita els ganxos de precomissió i missatge de comissió"
+msgstr "evita els lligams de precomissió i missatge de comissió"
#: builtin/commit.c:1625
msgid "show what would be committed"
#: builtin/commit.c:1637
msgid "bypass post-rewrite hook"
-msgstr "evita el ganxo de post escriptura"
+msgstr "evita el lligam de post escriptura"
#: builtin/commit.c:1642
msgid "ok to record an empty change"
"combined diff formats('-c' and '--cc') are not supported in\n"
"directory diff mode('-d' and '--dir-diff')."
msgstr ""
+"els formats de diff combinats ('-c' and '--cc') no són admesos \n"
+"en el mode diff per directoris ('-d' and '--dir-diff')."
#: builtin/difftool.c:633
#, c-format
"make 'git-difftool' exit when an invoked diff tool returns a non - zero exit "
"code"
msgstr ""
+"fes que 'git-difftool' surti quan l'eina de diff invocada torna un codi de sortida diferent a zero"
#: builtin/difftool.c:715
msgid "<command>"
"default for recursive fetching of submodules (lower priority than config "
"files)"
msgstr ""
+"per defecte per a l'obtenció recursiva de submòduls (prioritat més baixa que els fitxers de configuració)"
#: builtin/fetch.c:156 builtin/pull.c:212
msgid "accept refs that update .git/shallow"
#: builtin/hash-object.c:98
msgid "read the object from stdin"
-msgstr "llegeix l'objecte des d'stdin"
+msgstr "llegeix l'objecte des de stdin"
#: builtin/hash-object.c:100
msgid "store file as is without filters"
#: builtin/index-pack.c:1746
msgid "--stdin requires a git repository"
-msgstr "--stdin requereix d'un dipòsit git"
+msgstr "--stdin requereix un dipòsit git"
#: builtin/index-pack.c:1754
msgid "--verify with no packfile name given"
#: builtin/interpret-trailers.c:96
msgid "where to place the new trailer"
-msgstr ""
+msgstr "on ubica"
#: builtin/interpret-trailers.c:98
-#, fuzzy
msgid "action if trailer already exists"
-msgstr "l'arbre de treball '%s' ja existeix."
+msgstr "acció si el «trailer» ja existeix"
#: builtin/interpret-trailers.c:100
msgid "action if trailer is missing"
-msgstr ""
+msgstr "acció si el «trailer» falta"
#: builtin/interpret-trailers.c:102
-#, fuzzy
msgid "output only the trailers"
-msgstr "escurça els remolcs buits"
+msgstr "mostra només els «trailer»"
#: builtin/interpret-trailers.c:103
-#, fuzzy
msgid "do not apply config rules"
-msgstr "cerca les variables de configuració"
+msgstr "no apliquis les regles de configuració"
#: builtin/interpret-trailers.c:104
msgid "join whitespace-continued values"
-msgstr ""
+msgstr "uneix els valors continus amb espais en blanc"
#: builtin/interpret-trailers.c:105
msgid "set parsing options"
msgstr "remolcs a afegir"
#: builtin/interpret-trailers.c:117
-#, fuzzy
msgid "--trailer with --only-input does not make sense"
-msgstr "--name-only no té sentit"
+msgstr "--trailer amb --only-input no té sentit"
#: builtin/interpret-trailers.c:127
msgid "no input file given for in-place editing"
msgstr "permet fusionar històries no relacionades"
#: builtin/merge.c:240
-#, fuzzy
msgid "verify commit-msg hook"
-msgstr "evita els ganxos de precomissió i missatge de comissió"
+msgstr "verifica el lligam de missatge de comissió"
#: builtin/merge.c:265
msgid "could not run stash."
#: builtin/name-rev.c:403
msgid "read from stdin"
-msgstr "llegeix d'stdin"
+msgstr "llegeix de stdin"
#: builtin/name-rev.c:404
msgid "allow to print `undefined` names (default)"
#: builtin/notes.c:488
msgid "read objects from stdin"
-msgstr "llegeix els objectes des d'stdin"
+msgstr "llegeix els objectes des de stdin"
#: builtin/notes.c:490
msgid "load rewriting config for <command> (implies --stdin)"
#: builtin/pull.c:912
msgid "cannot rebase with locally recorded submodule modifications"
-msgstr ""
+msgstr "no es pot fer «rebase» amb modificacions als submòduls enregistrades localment"
#: builtin/push.c:17
msgid "git push [<options>] [<repository> [<refspec>...]]"
#: builtin/push.c:547
msgid "bypass pre-push hook"
-msgstr "evita el ganxo de prepujada"
+msgstr "evita el lligam de prepujada"
#: builtin/push.c:548
msgid "push missing but relevant tags"
msgstr "comprova la llista a fer"
#: builtin/rebase--helper.c:36
-#, fuzzy
msgid "skip unnecessary picks"
-msgstr "No s'ha pogut ometre ordres innecessaris d'elecció"
+msgstr "omet els «picks» no necessaris"
#: builtin/rebase--helper.c:38
msgid "rearrange fixup/squash lines"
-msgstr ""
+msgstr "reorganitza les línies «fixup/pick»"
#: builtin/receive-pack.c:29
msgid "git receive-pack <git-dir>"
"recomana això a menys que hàgiu decidit actualitzar el seu arbre en\n"
"alguna altra manera per a coincidir amb el que hàgiu pujat.\n"
"\n"
-"Per a silenciar aquest missatge i mantenir el el comportament\n"
+"Per a silenciar aquest missatge i mantenir el comportament\n"
"predeterminat, establiu la variable de configuració\n"
"'receive.denyCurrentBranch' a 'refuse'."
"Incremental repacks are incompatible with bitmap indexes. Use\n"
"--no-write-bitmap-index or disable the pack.writebitmaps configuration."
msgstr ""
+"Els re-empaquetaments incrementals són incompatibles amb els índexs de bitmaps. Useu\n"
+"--no-write-bitmap-index o inhabiliteu el paràmetre de configuració pack.writebitmaps."
#: builtin/repack.c:168
msgid "pack everything in a single pack"
#: builtin/reset.c:312
msgid "record only the fact that removed paths will be added later"
-msgstr "registra només el fet de que els camins eliminats s'afegiran després"
+msgstr "registra només el fet que els camins eliminats s'afegiran després"
#: builtin/reset.c:329
#, c-format
#: builtin/send-pack.c:177
msgid "read refs from stdin"
-msgstr "llegeix les referències des d'stdin"
+msgstr "llegeix les referències des de stdin"
#: builtin/send-pack.c:178
msgid "print status from remote helper"
#: builtin/shortlog.c:263
msgid "Group by committer rather than author"
-msgstr ""
+msgstr "Agrupa per «comiitter» en comptes de per autor"
#: builtin/shortlog.c:265
msgid "sort output according to the number of commits per author"
#: builtin/show-ref.c:176
msgid "show refs from stdin that aren't in local repository"
-msgstr "mostra les referències d'stdin que no siguin en el dipòsit local"
+msgstr "mostra les referències de stdin que no siguin en el dipòsit local"
#: builtin/stripspace.c:18
msgid "git stripspace [-s | --strip-comments]"
"could not lookup configuration '%s'. Assuming this repository is its own "
"authoritative upstream."
msgstr ""
+"no s'ha pogut trobar la configuració «%s». S'assumeix que aquest dipòsit és el seu dipòsit font autoritzat."
#: builtin/submodule--helper.c:400
#, c-format
#: builtin/update-ref.c:367
msgid "read updates from stdin"
-msgstr "llegeix les actualitzacions des d'stdin"
+msgstr "llegeix les actualitzacions des de stdin"
#: builtin/update-server-info.c:7
msgid "git update-server-info [--force]"
"Removing worktrees/%s: short read (expected %<PRIuMAX> bytes, read "
"%<PRIuMAX>)"
msgstr ""
+"S'estan suprimint els arbres de treball/%s: lectura curta (s'esperaven %<PRIuMAX> bytes, llegits "
+"%<PRIuMAX>)"
#: builtin/worktree.c:84
#, c-format
#: http.c:338
#, c-format
msgid "negative value for http.postbuffer; defaulting to %d"
-msgstr ""
+msgstr "valor negatiu per http.postbuffer; utilitzant el valor %d"
#: http.c:359
msgid "Delegation control is not supported with cURL < 7.22.0"
#: git-rebase.sh:214
msgid "The pre-rebase hook refused to rebase."
-msgstr "El ganxo pre-«rebase» ha refusat a fer «rebase»."
+msgstr "El lligam pre-«rebase» ha refusat a fer «rebase»."
#: git-rebase.sh:219
msgid "It looks like git-am is in progress. Cannot rebase."
#: git-stash.sh:641
msgid "The stash entry is kept in case you need it again."
msgstr ""
-"Es conserva l'entrada «stash» en cas de que la necessiteu altra vegada."
+"Es conserva l'entrada «stash» en cas que la necessiteu altra vegada."
#: git-stash.sh:650
#, sh-format
msgstr ""
"No s'ha pogut esmenar la comissió després d'escollir amb èxit $sha1... "
"$rest\n"
-"Això és probablement a causa d'un missatge de comissió buit, o el ganxo de\n"
-"precomissió ha fallat. Si el ganxo de precomissió ha fallat, pot ser que\n"
+"Això és probablement a causa d'un missatge de comissió buit, o el lligam de\n"
+"precomissió ha fallat. Si el lligam de precomissió ha fallat, pot ser que\n"
"necessiteu resoldre el problema abans que pugueu canviar el missatge de\n"
"comissió."
#: git-add--interactive.perl:196
#, perl-format
msgid "%12s %12s %s"
-msgstr ""
+msgstr "%12s %12s %s"
#: git-add--interactive.perl:197
msgid "staged"
"a - apply this hunk and all later hunks in the file\n"
"d - do not apply this hunk or any of the later hunks in the file"
msgstr ""
+"y - aplica aquest tros a l'índex\n"
+"n - no apliquis aquest tros a l'índex\n"
+"q - surt; no apliquis aquest tros ni cap dels pendents\n"
+"a - aplica aquest tros i tots els trossos posteriors en el fitxer\n"
+"d - no apliquis aquest tros ni cap dels trossos posteriors en el fitxer"
+
#: git-add--interactive.perl:1167
msgid ""
"a - discard this hunk and all later hunks in the file\n"
"d - do not discard this hunk or any of the later hunks in the file"
msgstr ""
+"y - descarta aquest tros de l'arbre de treball\n"
+"n - no descartis aquest tros des de l'arbre de treball\n"
+"q - surt; no apliquis aquest tros ni cap dels pendents\n"
+"a - descarta aquest tros i tots els trossos posteriors en el fitxer\n"
+"d - no descartis aquest tros ni cap dels trossos posteriors en el fitxer"
#: git-add--interactive.perl:1173
msgid ""
"a - discard this hunk and all later hunks in the file\n"
"d - do not discard this hunk or any of the later hunks in the file"
msgstr ""
+"y - descarta aquest tros de l'índex i de l'arbre de treball\n"
+"n - no descartis aquest tros des de l'índex i de l'arbre de treball\n"
+"q - surt; no apliquis aquest tros ni cap dels pendents\n"
+"a - descarta aquest tros i tots els trossos posteriors en el fitxer\n"
+"d - no descartis aquest tros ni cap dels trossos posteriors en el fitxer"
#: git-add--interactive.perl:1179
msgid ""
"a - apply this hunk and all later hunks in the file\n"
"d - do not apply this hunk or any of the later hunks in the file"
msgstr ""
+"y - aplica aquest tros a l'índex i l'arbre de treball\n"
+"n - no apliquis aquest tros des de l'índex i de l'arbre de treball\n"
+"q - surt; no apliquis aquest tros ni cap dels pendents\n"
+"a - aplica aquest tros i tots els trossos posteriors en el fitxer\n"
+"d - no apliquis aquest tros ni cap dels trossos posteriors en el fitxer"
#: git-add--interactive.perl:1188
msgid ""
"e - manually edit the current hunk\n"
"? - print help\n"
msgstr ""
+"g - selecciona el tros on voleu anar\n"
+"/ - cerca un tros que coincideixi amb l'expressió regular donada\n"
+"j - deixa aquest tros sense decidir, veure el tros sense decidir següent\n"
+"J - deixa aquest tros sense decidir, veure el tros següent\n"
+"k - deixa aquest tros sense decidir, veure el tros sense decidir anterior\n"
+"K - deixa aquest tros sense decidir, veure el tros anterior\n"
+"s - divideix el tros actual en trossos més petits\n"
+"e - edita manualment el tros actual\n"
+"? - mostra l'ajuda\n"
#: git-add--interactive.perl:1219
msgid "The selected hunks do not apply to the index!\n"
"add untracked - add contents of untracked files to the staged set of "
"changes\n"
msgstr ""
+"status - mostra els camins amb canvis\n"
+"update - afegeix l'estat de l'arbre de treball al conjunt de canvis «staged»\n"
+"revert - reverteix el conjunt de canvis de «staged» a la versió HEAD\n"
+"patch - selecciona trossos i actualitza'ls selectivament\n"
+"diff - mostra la diferència entre HEAD i l'índex\n"
+"add untracked - afegeix el contingut dels fitxers no seguits al conjunt de canvis «staged»\n"
#: git-add--interactive.perl:1641 git-add--interactive.perl:1646
#: git-add--interactive.perl:1649 git-add--interactive.perl:1656
"want to send.\n"
msgstr ""
+"S'ha refusat a enviar perquè el pedaç\n"
+"\t%s\n"
+"perquè la plantilla té l'assumpte '*** SUBJECT HERE ***'. Passeu --force si realment "
+"voleu enviar-lo.\n"
+
#: git-send-email.perl:847
msgid "To whom should the emails be sent (if anyone)?"
msgstr ""
#: git-send-email.perl:1431 git-send-email.perl:1435
#, perl-format
msgid "STARTTLS failed! %s"
-msgstr ""
+msgstr "STARTTLS ha fallat! %s"
#: git-send-email.perl:1445
msgid "Unable to initialize SMTP properly. Check config and use --smtp-debug."
return 0;
}
-/*
- * Opportunistically update the index but do not complain if we can't
- */
void update_index_if_able(struct index_state *istate, struct lock_file *lockfile)
{
if ((istate->cache_changed || has_racy_timestamp(istate)) &&
- verify_index(istate) &&
- write_locked_index(istate, lockfile, COMMIT_LOCK))
+ verify_index(istate))
+ write_locked_index(istate, lockfile, COMMIT_LOCK);
+ else
rollback_lock_file(lockfile);
}
+/*
+ * On success, `tempfile` is closed. If it is the temporary file
+ * of a `struct lock_file`, we will therefore effectively perform
+ * a 'close_lock_file_gently()`. Since that is an implementation
+ * detail of lockfiles, callers of `do_write_index()` should not
+ * rely on it.
+ */
static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
int strip_extensions)
{
return -1;
if (close_tempfile_gently(tempfile)) {
error(_("could not close '%s'"), tempfile->filename.buf);
- delete_tempfile(&tempfile);
return -1;
}
if (stat(tempfile->filename.buf, &st))
int ret = do_write_index(istate, lock->tempfile, 0);
if (ret)
return ret;
- assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
- (COMMIT_LOCK | CLOSE_LOCK));
if (flags & COMMIT_LOCK)
return commit_locked_index(lock);
- else if (flags & CLOSE_LOCK)
- return close_lock_file_gently(lock);
- else
- return ret;
+ return close_lock_file_gently(lock);
}
static int write_split_index(struct index_state *istate,
(istate->cache_changed & ~EXTMASK)) {
if (si)
hashclr(si->base_sha1);
- return do_write_locked_index(istate, lock, flags);
+ ret = do_write_locked_index(istate, lock, flags);
+ goto out;
}
if (getenv("GIT_TEST_SPLIT_INDEX")) {
if (new_shared_index) {
ret = write_shared_index(istate, lock, flags);
if (ret)
- return ret;
+ goto out;
}
ret = write_split_index(istate, lock, flags);
if (!ret && !new_shared_index)
freshen_shared_index(sha1_to_hex(si->base_sha1), 1);
+out:
+ if (flags & COMMIT_LOCK)
+ rollback_lock_file(lock);
return ret;
}
goto cleanup;
}
backend_data->packed_refs_locked = 1;
- ret = ref_transaction_prepare(packed_transaction, err);
+
+ if (is_packed_transaction_needed(refs->packed_ref_store,
+ packed_transaction)) {
+ ret = ref_transaction_prepare(packed_transaction, err);
+ } else {
+ /*
+ * We can skip rewriting the `packed-refs`
+ * file. But we do need to leave it locked, so
+ * that somebody else doesn't pack a reference
+ * that we are trying to delete.
+ */
+ if (ref_transaction_abort(packed_transaction, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+ backend_data->packed_transaction = NULL;
+ }
}
cleanup:
return -1;
}
+int is_packed_transaction_needed(struct ref_store *ref_store,
+ struct ref_transaction *transaction)
+{
+ struct packed_ref_store *refs = packed_downcast(
+ ref_store,
+ REF_STORE_READ,
+ "is_packed_transaction_needed");
+ struct strbuf referent = STRBUF_INIT;
+ size_t i;
+ int ret;
+
+ if (!is_lock_file_locked(&refs->lock))
+ BUG("is_packed_transaction_needed() called while unlocked");
+
+ /*
+ * We're only going to bother returning false for the common,
+ * trivial case that references are only being deleted, their
+ * old values are not being checked, and the old `packed-refs`
+ * file doesn't contain any of those reference(s). This gives
+ * false positives for some other cases that could
+ * theoretically be optimized away:
+ *
+ * 1. It could be that the old value is being verified without
+ * setting a new value. In this case, we could verify the
+ * old value here and skip the update if it agrees. If it
+ * disagrees, we could either let the update go through
+ * (the actual commit would re-detect and report the
+ * problem), or come up with a way of reporting such an
+ * error to *our* caller.
+ *
+ * 2. It could be that a new value is being set, but that it
+ * is identical to the current packed value of the
+ * reference.
+ *
+ * Neither of these cases will come up in the current code,
+ * because the only caller of this function passes to it a
+ * transaction that only includes `delete` updates with no
+ * `old_id`. Even if that ever changes, false positives only
+ * cause an optimization to be missed; they do not affect
+ * correctness.
+ */
+
+ /*
+ * Start with the cheap checks that don't require old
+ * reference values to be read:
+ */
+ for (i = 0; i < transaction->nr; i++) {
+ struct ref_update *update = transaction->updates[i];
+
+ if (update->flags & REF_HAVE_OLD)
+ /* Have to check the old value -> needed. */
+ return 1;
+
+ if ((update->flags & REF_HAVE_NEW) && !is_null_oid(&update->new_oid))
+ /* Have to set a new value -> needed. */
+ return 1;
+ }
+
+ /*
+ * The transaction isn't checking any old values nor is it
+ * setting any nonzero new values, so it still might be able
+ * to be skipped. Now do the more expensive check: the update
+ * is needed if any of the updates is a delete, and the old
+ * `packed-refs` file contains a value for that reference.
+ */
+ ret = 0;
+ for (i = 0; i < transaction->nr; i++) {
+ struct ref_update *update = transaction->updates[i];
+ unsigned int type;
+ struct object_id oid;
+
+ if (!(update->flags & REF_HAVE_NEW))
+ /*
+ * This reference isn't being deleted -> not
+ * needed.
+ */
+ continue;
+
+ if (!refs_read_raw_ref(ref_store, update->refname,
+ &oid, &referent, &type) ||
+ errno != ENOENT) {
+ /*
+ * We have to actually delete that reference
+ * -> this transaction is needed.
+ */
+ ret = 1;
+ break;
+ }
+ }
+
+ strbuf_release(&referent);
+ return ret;
+}
+
struct packed_transaction_backend_data {
/* True iff the transaction owns the packed-refs lock. */
int own_lock;
void packed_refs_unlock(struct ref_store *ref_store);
int packed_refs_is_locked(struct ref_store *ref_store);
+/*
+ * Return true if `transaction` really needs to be carried out against
+ * the specified packed_ref_store, or false if it can be skipped
+ * (i.e., because it is an obvious NOOP). `ref_store` must be locked
+ * before calling this function.
+ */
+int is_packed_transaction_needed(struct ref_store *ref_store,
+ struct ref_transaction *transaction);
+
#endif /* REFS_PACKED_BACKEND_H */
* if the whole diff is removal of old data, and otherwise
* REV_TREE_DIFFERENT (of course if the trees are the same we
* want REV_TREE_SAME).
- * That means that once we get to REV_TREE_DIFFERENT, we do not
- * have to look any further.
+ *
+ * The only time we care about the distinction is when
+ * remove_empty_trees is in effect, in which case we care only about
+ * whether the whole change is REV_TREE_NEW, or if there's another type
+ * of change. Which means we can stop the diff early in either of these
+ * cases:
+ *
+ * 1. We're not using remove_empty_trees at all.
+ *
+ * 2. We saw anything except REV_TREE_NEW.
*/
static int tree_difference = REV_TREE_SAME;
const char *fullpath, unsigned dirty_submodule)
{
int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
+ struct rev_info *revs = options->change_fn_data;
tree_difference |= diff;
- if (tree_difference == REV_TREE_DIFFERENT)
- DIFF_OPT_SET(options, HAS_CHANGES);
+ if (!revs->remove_empty_trees || tree_difference != REV_TREE_NEW)
+ options->flags.has_changes = 1;
}
static void file_change(struct diff_options *options,
unsigned old_dirty_submodule, unsigned new_dirty_submodule)
{
tree_difference = REV_TREE_DIFFERENT;
- DIFF_OPT_SET(options, HAS_CHANGES);
+ options->flags.has_changes = 1;
}
static int rev_compare_tree(struct rev_info *revs,
}
tree_difference = REV_TREE_SAME;
- DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
+ revs->pruning.flags.has_changes = 0;
if (diff_tree_oid(&t1->object.oid, &t2->object.oid, "",
&revs->pruning) < 0)
return REV_TREE_DIFFERENT;
return 0;
tree_difference = REV_TREE_SAME;
- DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
+ revs->pruning.flags.has_changes = 0;
retval = diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning);
return retval >= 0 && (tree_difference == REV_TREE_SAME);
revs->abbrev = DEFAULT_ABBREV;
revs->ignore_merges = 1;
revs->simplify_history = 1;
- DIFF_OPT_SET(&revs->pruning, RECURSIVE);
- DIFF_OPT_SET(&revs->pruning, QUICK);
+ revs->pruning.flags.recursive = 1;
+ revs->pruning.flags.quick = 1;
revs->pruning.add_remove = file_add_remove;
revs->pruning.change = file_change;
+ revs->pruning.change_fn_data = revs;
revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
revs->dense = 1;
revs->prefix = prefix;
die("--unpacked=<packfile> no longer supported.");
} else if (!strcmp(arg, "-r")) {
revs->diff = 1;
- DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
+ revs->diffopt.flags.recursive = 1;
} else if (!strcmp(arg, "-t")) {
revs->diff = 1;
- DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
- DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
+ revs->diffopt.flags.recursive = 1;
+ revs->diffopt.flags.tree_in_recursive = 1;
} else if (!strcmp(arg, "-m")) {
revs->ignore_merges = 0;
} else if (!strcmp(arg, "-c")) {
revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE;
} else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
revs->grep_filter.ignore_case = 1;
- DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
+ revs->diffopt.flags.pickaxe_ignore_case = 1;
} else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_FIXED;
} else if (!strcmp(arg, "--perl-regexp") || !strcmp(arg, "-P")) {
/* Pickaxe, diff-filter and rename following need diffs */
if (revs->diffopt.pickaxe ||
revs->diffopt.filter ||
- DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
+ revs->diffopt.flags.follow_renames)
revs->diff = 1;
if (revs->topo_order)
if (revs->prune_data.nr) {
copy_pathspec(&revs->pruning.pathspec, &revs->prune_data);
/* Can't prune commits with rename following: the paths change.. */
- if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
+ if (!revs->diffopt.flags.follow_renames)
revs->prune = 1;
if (!revs->full_diff)
copy_pathspec(&revs->diffopt.pathspec,
#include "argv-array.h"
#include "thread-utils.h"
#include "strbuf.h"
+#include "string-list.h"
void child_process_init(struct child_process *child)
{
strbuf_reset(&path);
strbuf_git_path(&path, "hooks/%s", name);
if (access(path.buf, X_OK) < 0) {
+ int err = errno;
+
#ifdef STRIP_EXTENSION
strbuf_addstr(&path, STRIP_EXTENSION);
if (access(path.buf, X_OK) >= 0)
return path.buf;
+ if (errno == EACCES)
+ err = errno;
#endif
+
+ if (err == EACCES && advice_ignored_hook) {
+ static struct string_list advise_given = STRING_LIST_INIT_DUP;
+
+ if (!string_list_lookup(&advise_given, name)) {
+ string_list_insert(&advise_given, name);
+ advise(_("The '%s' hook was ignored because "
+ "it's not set as executable.\n"
+ "You can disable this warning with "
+ "`git config advice.ignoredHook false`."),
+ path.buf);
+ }
+ }
return NULL;
}
return path.buf;
unborn = get_oid("HEAD", &head);
if (unborn)
oidcpy(&head, &empty_tree_oid);
- if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0, 0))
+ if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD",
+ NULL, 0))
return error_dirty_index(opts);
}
discard_cache();
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, NULL, NULL, NULL);
if (the_index.cache_changed && index_fd >= 0) {
if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) {
- rollback_lock_file(&index_lock);
return error(_("git %s: failed to refresh the index"),
_(action_name(opts)));
}
static int do_exec(const char *command_line)
{
+ struct argv_array child_env = ARGV_ARRAY_INIT;
const char *child_argv[] = { NULL, NULL };
int dirty, status;
fprintf(stderr, "Executing: %s\n", command_line);
child_argv[0] = command_line;
- status = run_command_v_opt(child_argv, RUN_USING_SHELL);
+ argv_array_pushf(&child_env, "GIT_DIR=%s", absolute_path(get_git_dir()));
+ status = run_command_v_opt_cd_env(child_argv, RUN_USING_SHELL, NULL,
+ child_env.argv);
/* force re-reading of the cache */
if (discard_cache() < 0 || read_cache() < 0)
status = 1;
}
+ argv_array_clear(&child_env);
+
return status;
}
if (res)
goto release_todo_list;
}
- if (index_differs_from("HEAD", 0, 0)) {
+ if (index_differs_from("HEAD", NULL, 0)) {
res = error_dirty_index(opts);
goto release_todo_list;
}
return res;
}
+static int rewrite_file(const char *path, const char *buf, size_t len)
+{
+ int rc = 0;
+ int fd = open(path, O_WRONLY | O_TRUNC);
+ if (fd < 0)
+ return error_errno(_("could not open '%s' for writing"), path);
+ if (write_in_full(fd, buf, len) < 0)
+ rc = error_errno(_("could not write to '%s'"), path);
+ if (close(fd) && !rc)
+ rc = error_errno(_("could not close '%s'"), path);
+ return rc;
+}
+
/* skip picking commits whose parents are unchanged */
int skip_unnecessary_picks(void)
{
}
close(fd);
- fd = open(rebase_path_todo(), O_WRONLY, 0666);
- if (fd < 0) {
- error_errno(_("could not open '%s' for writing"),
- rebase_path_todo());
+ if (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,
+ todo_list.buf.len - offset) < 0) {
todo_list_release(&todo_list);
return -1;
}
- if (write_in_full(fd, todo_list.buf.buf + offset,
- todo_list.buf.len - offset) < 0) {
- error_errno(_("could not write to '%s'"),
- rebase_path_todo());
- close(fd);
- todo_list_release(&todo_list);
- return -1;
- }
- if (ftruncate(fd, todo_list.buf.len - offset) < 0) {
- error_errno(_("could not truncate '%s'"),
- rebase_path_todo());
- todo_list_release(&todo_list);
- close(fd);
- return -1;
- }
- close(fd);
todo_list.current = i;
if (is_fixup(peek_command(&todo_list, 0)))
}
}
- fd = open(todo_file, O_WRONLY);
- if (fd < 0)
- res = error_errno(_("could not open '%s'"), todo_file);
- else if (write(fd, buf.buf, buf.len) < 0)
- res = error_errno(_("could not write to '%s'"), todo_file);
- else if (ftruncate(fd, buf.len) < 0)
- res = error_errno(_("could not truncate '%s'"),
- todo_file);
- close(fd);
+ res = rewrite_file(todo_file, buf.buf, buf.len);
strbuf_release(&buf);
}
size_t len;
/* Check worktree-related signatures */
- strbuf_addf(&path, "%s/HEAD", suspect);
+ strbuf_addstr(&path, suspect);
+ strbuf_complete(&path, '/');
+ strbuf_addstr(&path, "HEAD");
if (validate_headref(path.buf))
goto done;
void add_to_alternates_file(const char *reference)
{
- struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+ struct lock_file lock = LOCK_INIT;
char *alts = git_pathdup("objects/info/alternates");
FILE *in, *out;
+ int found = 0;
- hold_lock_file_for_update(lock, alts, LOCK_DIE_ON_ERROR);
- out = fdopen_lock_file(lock, "w");
+ hold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR);
+ out = fdopen_lock_file(&lock, "w");
if (!out)
die_errno("unable to fdopen alternates lockfile");
in = fopen(alts, "r");
if (in) {
struct strbuf line = STRBUF_INIT;
- int found = 0;
while (strbuf_getline(&line, in) != EOF) {
if (!strcmp(reference, line.buf)) {
strbuf_release(&line);
fclose(in);
-
- if (found) {
- rollback_lock_file(lock);
- lock = NULL;
- }
}
else if (errno != ENOENT)
die_errno("unable to read alternates file");
- if (lock) {
+ if (found) {
+ rollback_lock_file(&lock);
+ } else {
fprintf_or_die(out, "%s\n", reference);
- if (commit_lock_file(lock))
+ if (commit_lock_file(&lock))
die_errno("unable to move new alternates file into place");
if (alt_odb_tail)
link_alt_odb_entries(reference, '\n', NULL, 0);
DIR *dir;
struct dirent *de;
int r = 0;
+ struct object_id oid;
if (subdir_nr > 0xff)
BUG("invalid loose object subdirectory: %x", subdir_nr);
return r;
}
+ oid.hash[0] = subdir_nr;
+
while ((de = readdir(dir))) {
if (is_dot_or_dotdot(de->d_name))
continue;
strbuf_setlen(path, baselen);
strbuf_addf(path, "/%s", de->d_name);
- if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2) {
- char hex[GIT_MAX_HEXSZ+1];
- struct object_id oid;
-
- xsnprintf(hex, sizeof(hex), "%02x%s",
- subdir_nr, de->d_name);
- if (!get_oid_hex(hex, &oid)) {
- if (obj_cb) {
- r = obj_cb(&oid, path->buf, data);
- if (r)
- break;
- }
- continue;
+ if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2 &&
+ !hex_to_bytes(oid.hash + 1, de->d_name,
+ GIT_SHA1_RAWSZ - 1)) {
+ if (obj_cb) {
+ r = obj_cb(&oid, path->buf, data);
+ if (r)
+ break;
}
+ continue;
}
if (cruft_cb) {
uint32_t num, last, i, first = 0;
const struct object_id *current = NULL;
- open_pack_index(p);
+ if (open_pack_index(p) || !p->num_objects)
+ return;
+
num = p->num_objects;
last = num;
while (first < last) {
return r;
}
-int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
+struct min_abbrev_data {
+ unsigned int init_len;
+ unsigned int cur_len;
+ char *hex;
+ const unsigned char *hash;
+};
+
+static inline char get_hex_char_from_oid(const struct object_id *oid,
+ unsigned int pos)
+{
+ static const char hex[] = "0123456789abcdef";
+
+ if ((pos & 1) == 0)
+ return hex[oid->hash[pos >> 1] >> 4];
+ else
+ return hex[oid->hash[pos >> 1] & 0xf];
+}
+
+static int extend_abbrev_len(const struct object_id *oid, void *cb_data)
+{
+ struct min_abbrev_data *mad = cb_data;
+
+ unsigned int i = mad->init_len;
+ while (mad->hex[i] && mad->hex[i] == get_hex_char_from_oid(oid, i))
+ i++;
+
+ if (i < GIT_MAX_RAWSZ && i >= mad->cur_len)
+ mad->cur_len = i + 1;
+
+ return 0;
+}
+
+static void find_abbrev_len_for_pack(struct packed_git *p,
+ struct min_abbrev_data *mad)
+{
+ int match = 0;
+ uint32_t num, last, first = 0;
+ struct object_id oid;
+
+ if (open_pack_index(p) || !p->num_objects)
+ return;
+
+ num = p->num_objects;
+ last = num;
+ while (first < last) {
+ uint32_t mid = first + (last - first) / 2;
+ const unsigned char *current;
+ int cmp;
+
+ current = nth_packed_object_sha1(p, mid);
+ cmp = hashcmp(mad->hash, current);
+ if (!cmp) {
+ match = 1;
+ first = mid;
+ break;
+ }
+ if (cmp > 0) {
+ first = mid + 1;
+ continue;
+ }
+ last = mid;
+ }
+
+ /*
+ * first is now the position in the packfile where we would insert
+ * mad->hash if it does not exist (or the position of mad->hash if
+ * it does exist). Hence, we consider a maximum of three objects
+ * nearby for the abbreviation length.
+ */
+ mad->init_len = 0;
+ if (!match) {
+ nth_packed_object_oid(&oid, p, first);
+ extend_abbrev_len(&oid, mad);
+ } else if (first < num - 1) {
+ nth_packed_object_oid(&oid, p, first + 1);
+ extend_abbrev_len(&oid, mad);
+ }
+ if (first > 0) {
+ nth_packed_object_oid(&oid, p, first - 1);
+ extend_abbrev_len(&oid, mad);
+ }
+ mad->init_len = mad->cur_len;
+}
+
+static void find_abbrev_len_packed(struct min_abbrev_data *mad)
{
- int status, exists;
+ struct packed_git *p;
+
+ prepare_packed_git();
+ for (p = packed_git; p; p = p->next)
+ find_abbrev_len_for_pack(p, mad);
+}
+int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
+{
+ struct disambiguate_state ds;
+ struct min_abbrev_data mad;
+ struct object_id oid_ret;
if (len < 0) {
unsigned long count = approximate_object_count();
/*
sha1_to_hex_r(hex, sha1);
if (len == GIT_SHA1_HEXSZ || !len)
return GIT_SHA1_HEXSZ;
- exists = has_sha1_file(sha1);
- while (len < GIT_SHA1_HEXSZ) {
- struct object_id oid_ret;
- status = get_short_oid(hex, len, &oid_ret, GET_OID_QUIETLY);
- if (exists
- ? !status
- : status == SHORT_NAME_NOT_FOUND) {
- hex[len] = 0;
- return len;
- }
- len++;
- }
- return len;
+
+ mad.init_len = len;
+ mad.cur_len = len;
+ mad.hex = hex;
+ mad.hash = sha1;
+
+ find_abbrev_len_packed(&mad);
+
+ if (init_object_disambiguation(hex, mad.cur_len, &ds) < 0)
+ return -1;
+
+ ds.fn = extend_abbrev_len;
+ ds.always_call_fn = 1;
+ ds.cb_data = (void *)&mad;
+
+ find_short_object_filename(&ds);
+ (void)finish_object_disambiguation(&ds, &oid_ret);
+
+ hex[mad.cur_len] = 0;
+ return mad.cur_len;
}
const char *find_unique_abbrev(const unsigned char *sha1, int len)
int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
{
- strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL);
+ if (startup_info->have_repository)
+ strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL);
+ else
+ strbuf_addstr(sb, name);
if (name[0] == '-')
return -1;
strbuf_splice(sb, 0, 0, "refs/heads/", 11);
int recommend_shallow;
};
+#define SUBMODULE_INIT { NULL, NULL, NULL, RECURSE_SUBMODULES_NONE, \
+ NULL, NULL, SUBMODULE_UPDATE_STRATEGY_INIT, {0}, -1 };
+
struct submodule_cache;
struct repository;
#include "parse-options.h"
static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
-static struct string_list changed_submodule_paths = STRING_LIST_INIT_DUP;
+static struct string_list changed_submodule_names = STRING_LIST_INIT_DUP;
static int initialized_fetch_ref_tips;
static struct oid_array ref_tips_before_fetch;
static struct oid_array ref_tips_after_fetch;
if (ignore)
handle_ignore_submodules_arg(diffopt, ignore);
else if (is_gitmodules_unmerged(&the_index))
- DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
+ diffopt->flags.ignore_submodules = 1;
}
}
void handle_ignore_submodules_arg(struct diff_options *diffopt,
const char *arg)
{
- DIFF_OPT_CLR(diffopt, IGNORE_SUBMODULES);
- DIFF_OPT_CLR(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
- DIFF_OPT_CLR(diffopt, IGNORE_DIRTY_SUBMODULES);
+ diffopt->flags.ignore_submodules = 0;
+ diffopt->flags.ignore_untracked_in_submodules = 0;
+ diffopt->flags.ignore_dirty_submodules = 0;
if (!strcmp(arg, "all"))
- DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
+ diffopt->flags.ignore_submodules = 1;
else if (!strcmp(arg, "untracked"))
- DIFF_OPT_SET(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+ diffopt->flags.ignore_untracked_in_submodules = 1;
else if (!strcmp(arg, "dirty"))
- DIFF_OPT_SET(diffopt, IGNORE_DIRTY_SUBMODULES);
+ diffopt->flags.ignore_dirty_submodules = 1;
else if (strcmp(arg, "none"))
die("bad --ignore-submodules argument: %s", arg);
}
argv_array_pushf(&cp.args, "--color=%s", want_color(o->use_color) ?
"always" : "never");
- if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+ if (o->flags.reverse_diff) {
argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
o->b_prefix, path);
argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
}
static struct oid_array *submodule_commits(struct string_list *submodules,
- const char *path)
+ const char *name)
{
struct string_list_item *item;
- item = string_list_insert(submodules, path);
+ item = string_list_insert(submodules, name);
if (item->util)
return (struct oid_array *) item->util;
return (struct oid_array *) item->util;
}
+struct collect_changed_submodules_cb_data {
+ struct string_list *changed;
+ const struct object_id *commit_oid;
+};
+
+/*
+ * this would normally be two functions: default_name_from_path() and
+ * path_from_default_name(). Since the default name is the same as
+ * the submodule path we can get away with just one function which only
+ * checks whether there is a submodule in the working directory at that
+ * location.
+ */
+static const char *default_name_or_path(const char *path_or_name)
+{
+ int error_code;
+
+ if (!is_submodule_populated_gently(path_or_name, &error_code))
+ return NULL;
+
+ return path_or_name;
+}
+
static void collect_changed_submodules_cb(struct diff_queue_struct *q,
struct diff_options *options,
void *data)
{
+ struct collect_changed_submodules_cb_data *me = data;
+ struct string_list *changed = me->changed;
+ const struct object_id *commit_oid = me->commit_oid;
int i;
- struct string_list *changed = data;
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
struct oid_array *commits;
+ const struct submodule *submodule;
+ const char *name;
+
if (!S_ISGITLINK(p->two->mode))
continue;
- if (S_ISGITLINK(p->one->mode)) {
- /*
- * NEEDSWORK: We should honor the name configured in
- * the .gitmodules file of the commit we are examining
- * here to be able to correctly follow submodules
- * being moved around.
- */
- commits = submodule_commits(changed, p->two->path);
- oid_array_append(commits, &p->two->oid);
- } else {
- /* Submodule is new or was moved here */
- /*
- * NEEDSWORK: When the .git directories of submodules
- * live inside the superprojects .git directory some
- * day we should fetch new submodules directly into
- * that location too when config or options request
- * that so they can be checked out from there.
- */
- continue;
+ submodule = submodule_from_path(commit_oid, p->two->path);
+ if (submodule)
+ name = submodule->name;
+ else {
+ name = default_name_or_path(p->two->path);
+ /* make sure name does not collide with existing one */
+ submodule = submodule_from_name(commit_oid, name);
+ if (submodule) {
+ warning("Submodule in commit %s at path: "
+ "'%s' collides with a submodule named "
+ "the same. Skipping it.",
+ oid_to_hex(commit_oid), name);
+ name = NULL;
+ }
}
+
+ if (!name)
+ continue;
+
+ commits = submodule_commits(changed, name);
+ oid_array_append(commits, &p->two->oid);
}
}
while ((commit = get_revision(&rev))) {
struct rev_info diff_rev;
+ struct collect_changed_submodules_cb_data data;
+ data.changed = changed;
+ data.commit_oid = &commit->object.oid;
init_revisions(&diff_rev, NULL);
diff_rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
diff_rev.diffopt.format_callback = collect_changed_submodules_cb;
- diff_rev.diffopt.format_callback_data = changed;
+ diff_rev.diffopt.format_callback_data = &data;
diff_tree_combined_merge(commit, 1, &diff_rev);
}
const char *remotes_name, struct string_list *needs_pushing)
{
struct string_list submodules = STRING_LIST_INIT_DUP;
- struct string_list_item *submodule;
+ struct string_list_item *name;
struct argv_array argv = ARGV_ARRAY_INIT;
/* argv.argv[0] will be ignored by setup_revisions */
collect_changed_submodules(&submodules, &argv);
- for_each_string_list_item(submodule, &submodules) {
- struct oid_array *commits = submodule->util;
- const char *path = submodule->string;
+ for_each_string_list_item(name, &submodules) {
+ struct oid_array *commits = name->util;
+ const struct submodule *submodule;
+ const char *path = NULL;
+
+ submodule = submodule_from_name(&null_oid, name->string);
+ if (submodule)
+ path = submodule->path;
+ else
+ path = default_name_or_path(name->string);
+
+ if (!path)
+ continue;
if (submodule_needs_pushing(path, commits))
string_list_insert(needs_pushing, path);
{
struct argv_array argv = ARGV_ARRAY_INIT;
struct string_list changed_submodules = STRING_LIST_INIT_DUP;
- const struct string_list_item *item;
+ const struct string_list_item *name;
/* No need to check if there are no submodules configured */
if (!submodule_from_path(NULL, NULL))
/*
* Collect all submodules (whether checked out or not) for which new
- * commits have been recorded upstream in "changed_submodule_paths".
+ * commits have been recorded upstream in "changed_submodule_names".
*/
collect_changed_submodules(&changed_submodules, &argv);
- for_each_string_list_item(item, &changed_submodules) {
- struct oid_array *commits = item->util;
- const char *path = item->string;
+ for_each_string_list_item(name, &changed_submodules) {
+ struct oid_array *commits = name->util;
+ const struct submodule *submodule;
+ const char *path = NULL;
+
+ submodule = submodule_from_name(&null_oid, name->string);
+ if (submodule)
+ path = submodule->path;
+ else
+ path = default_name_or_path(name->string);
+
+ if (!path)
+ continue;
if (!submodule_has_commits(path, commits))
- string_list_append(&changed_submodule_paths, path);
+ string_list_append(&changed_submodule_names, name->string);
}
free_submodules_oids(&changed_submodules);
};
#define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0}
+static int get_fetch_recurse_config(const struct submodule *submodule,
+ struct submodule_parallel_fetch *spf)
+{
+ if (spf->command_line_option != RECURSE_SUBMODULES_DEFAULT)
+ return spf->command_line_option;
+
+ if (submodule) {
+ char *key;
+ const char *value;
+
+ int fetch_recurse = submodule->fetch_recurse;
+ key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name);
+ if (!repo_config_get_string_const(the_repository, key, &value)) {
+ fetch_recurse = parse_fetch_recurse_submodules_arg(key, value);
+ }
+ free(key);
+
+ if (fetch_recurse != RECURSE_SUBMODULES_NONE)
+ /* local config overrules everything except commandline */
+ return fetch_recurse;
+ }
+
+ return spf->default_option;
+}
+
static int get_next_submodule(struct child_process *cp,
struct strbuf *err, void *data, void **task_cb)
{
const struct cache_entry *ce = active_cache[spf->count];
const char *git_dir, *default_argv;
const struct submodule *submodule;
+ struct submodule default_submodule = SUBMODULE_INIT;
if (!S_ISGITLINK(ce->ce_mode))
continue;
submodule = submodule_from_path(&null_oid, ce->name);
-
- default_argv = "yes";
- if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) {
- int fetch_recurse = RECURSE_SUBMODULES_NONE;
-
- if (submodule) {
- char *key;
- const char *value;
-
- fetch_recurse = submodule->fetch_recurse;
- key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name);
- if (!repo_config_get_string_const(the_repository, key, &value)) {
- fetch_recurse = parse_fetch_recurse_submodules_arg(key, value);
- }
- free(key);
+ if (!submodule) {
+ const char *name = default_name_or_path(ce->name);
+ if (name) {
+ default_submodule.path = default_submodule.name = name;
+ submodule = &default_submodule;
}
+ }
- if (fetch_recurse != RECURSE_SUBMODULES_NONE) {
- if (fetch_recurse == RECURSE_SUBMODULES_OFF)
- continue;
- if (fetch_recurse == RECURSE_SUBMODULES_ON_DEMAND) {
- if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
- continue;
- default_argv = "on-demand";
- }
- } else {
- if (spf->default_option == RECURSE_SUBMODULES_OFF)
- continue;
- if (spf->default_option == RECURSE_SUBMODULES_ON_DEMAND) {
- if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
- continue;
- default_argv = "on-demand";
- }
- }
- } else if (spf->command_line_option == RECURSE_SUBMODULES_ON_DEMAND) {
- if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
+ switch (get_fetch_recurse_config(submodule, spf))
+ {
+ default:
+ case RECURSE_SUBMODULES_DEFAULT:
+ case RECURSE_SUBMODULES_ON_DEMAND:
+ if (!submodule || !unsorted_string_list_lookup(&changed_submodule_names,
+ submodule->name))
continue;
default_argv = "on-demand";
+ break;
+ case RECURSE_SUBMODULES_ON:
+ default_argv = "yes";
+ break;
+ case RECURSE_SUBMODULES_OFF:
+ continue;
}
strbuf_addf(&submodule_path, "%s/%s", spf->work_tree, ce->name);
argv_array_clear(&spf.args);
out:
- string_list_clear(&changed_submodule_paths, 1);
+ string_list_clear(&changed_submodule_names, 1);
return spf.result;
}
ref = refs_resolve_ref_unsafe(refs, refname, resolve_flags,
&oid, &flags);
- printf("%s %s 0x%x\n", oid_to_hex(&oid), ref, flags);
+ printf("%s %s 0x%x\n", oid_to_hex(&oid), ref ? ref : "(null)", flags);
return ref ? 0 : 1;
}
reject $1 https example.com user2
reject $1 http path.tld user
reject $1 https timeout.tld user
+ reject $1 https sso.tld
}
reject() {
password=pass2
EOF
'
+
+ test_expect_success "helper ($HELPER) can store empty username" '
+ check approve $HELPER <<-\EOF &&
+ protocol=https
+ host=sso.tld
+ username=
+ password=
+ EOF
+ check fill $HELPER <<-\EOF
+ protocol=https
+ host=sso.tld
+ --
+ protocol=https
+ host=sso.tld
+ username=
+ password=
+ EOF
+ '
}
helper_test_timeout() {
git log -M -L 1:"$file" >/dev/null
'
+test_perf 'git log --oneline --raw --parents' '
+ git log --oneline --raw --parents >/dev/null
+'
+
test_done
. ./test-lib.sh
+try_local_x () {
+ local x="local" &&
+ echo "$x"
+}
+
+# This test is an experiment to check whether any Git users are using
+# Shells that don't support the "local" keyword. "local" is not
+# POSIX-standard, but it is very widely supported by POSIX-compliant
+# shells, and if it doesn't cause problems for people, we would like
+# to be able to use it in Git code.
+#
+# For now, this is the only test that requires "local". If your shell
+# fails this test, you can ignore the failure, but please report the
+# problem to the Git mailing list <git@vger.kernel.org>, as it might
+# convince us to continue avoiding the use of "local".
+test_expect_success 'verify that the running shell supports "local"' '
+ x="notlocal" &&
+ echo "local" >expected1 &&
+ try_local_x >actual1 &&
+ test_cmp expected1 actual1 &&
+ echo "notlocal" >expected2 &&
+ echo "$x" >actual2 &&
+ test_cmp expected2 actual2
+'
+
################################################################
# git init has been done in an empty repository.
# make sure it is empty.
)
'
+test_expect_success MINGW 'redirect std handles' '
+ GIT_REDIRECT_STDOUT=output.txt git rev-parse --git-dir &&
+ test .git = "$(cat output.txt)" &&
+ test -z "$(GIT_REDIRECT_STDOUT=off git rev-parse --git-dir)" &&
+ test_must_fail env \
+ GIT_REDIRECT_STDOUT=output.txt \
+ GIT_REDIRECT_STDERR="2>&1" \
+ git rev-parse --git-dir --verify refs/invalid &&
+ printf ".git\nfatal: Needed a single revision\n" >expect &&
+ test_cmp expect output.txt
+'
+
test_done
echo >.gitattributes &&
git checkout -b master &&
git add .gitattributes &&
- git commit -m "add .gitattributes" "" &&
+ git commit -m "add .gitattributes" . &&
printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\nLINETWO\nLINETHREE" >LF &&
printf "\$Id: 0000000000000000000000000000000000000000 \$\r\nLINEONE\r\nLINETWO\r\nLINETHREE" >CRLF &&
printf "\$Id: 0000000000000000000000000000000000000000 \$\nLINEONE\r\nLINETWO\nLINETHREE" >CRLF_mix_LF &&
refname2=$(git check-ref-format --branch @{-2}) &&
test "$refname2" = master'
+test_expect_success 'check-ref-format --branch -naster' '
+ test_must_fail git check-ref-format --branch -naster >actual &&
+ test_must_be_empty actual
+'
+
test_expect_success 'check-ref-format --branch from subdir' '
mkdir subdir &&
test "$refname" = "$sha1"
'
+test_expect_success 'check-ref-format --branch @{-1} from non-repo' '
+ nongit test_must_fail git check-ref-format --branch @{-1} >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'check-ref-format --branch master from non-repo' '
+ echo master >expect &&
+ nongit git check-ref-format --branch master >actual &&
+ test_cmp expect actual
+'
+
valid_ref_normalized() {
prereq=
case $1 in
--- /dev/null
+#!/bin/sh
+
+test_description='avoid rewriting packed-refs unnecessarily'
+
+. ./test-lib.sh
+
+# Add an identifying mark to the packed-refs file header line. This
+# shouldn't upset readers, and it should be omitted if the file is
+# ever rewritten.
+mark_packed_refs () {
+ sed -e "s/^\(#.*\)/\1 t1409 /" <.git/packed-refs >.git/packed-refs.new &&
+ mv .git/packed-refs.new .git/packed-refs
+}
+
+# Verify that the packed-refs file is still marked.
+check_packed_refs_marked () {
+ grep -q '^#.* t1409 ' .git/packed-refs
+}
+
+test_expect_success 'setup' '
+ git commit --allow-empty -m "Commit A" &&
+ A=$(git rev-parse HEAD) &&
+ git commit --allow-empty -m "Commit B" &&
+ B=$(git rev-parse HEAD) &&
+ git commit --allow-empty -m "Commit C" &&
+ C=$(git rev-parse HEAD)
+'
+
+test_expect_success 'do not create packed-refs file gratuitously' '
+ test_must_fail test -f .git/packed-refs &&
+ git update-ref refs/heads/foo $A &&
+ test_must_fail test -f .git/packed-refs &&
+ git update-ref refs/heads/foo $B &&
+ test_must_fail test -f .git/packed-refs &&
+ git update-ref refs/heads/foo $C $B &&
+ test_must_fail test -f .git/packed-refs &&
+ git update-ref -d refs/heads/foo &&
+ test_must_fail test -f .git/packed-refs
+'
+
+test_expect_success 'check that marking the packed-refs file works' '
+ git for-each-ref >expected &&
+ git pack-refs --all &&
+ mark_packed_refs &&
+ check_packed_refs_marked &&
+ git for-each-ref >actual &&
+ test_cmp expected actual &&
+ git pack-refs --all &&
+ test_must_fail check_packed_refs_marked &&
+ git for-each-ref >actual2 &&
+ test_cmp expected actual2
+'
+
+test_expect_success 'leave packed-refs untouched on update of packed' '
+ git update-ref refs/heads/packed-update $A &&
+ git pack-refs --all &&
+ mark_packed_refs &&
+ git update-ref refs/heads/packed-update $B &&
+ check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on checked update of packed' '
+ git update-ref refs/heads/packed-checked-update $A &&
+ git pack-refs --all &&
+ mark_packed_refs &&
+ git update-ref refs/heads/packed-checked-update $B $A &&
+ check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on verify of packed' '
+ git update-ref refs/heads/packed-verify $A &&
+ git pack-refs --all &&
+ mark_packed_refs &&
+ echo "verify refs/heads/packed-verify $A" | git update-ref --stdin &&
+ check_packed_refs_marked
+'
+
+test_expect_success 'touch packed-refs on delete of packed' '
+ git update-ref refs/heads/packed-delete $A &&
+ git pack-refs --all &&
+ mark_packed_refs &&
+ git update-ref -d refs/heads/packed-delete &&
+ test_must_fail check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on update of loose' '
+ git pack-refs --all &&
+ git update-ref refs/heads/loose-update $A &&
+ mark_packed_refs &&
+ git update-ref refs/heads/loose-update $B &&
+ check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on checked update of loose' '
+ git pack-refs --all &&
+ git update-ref refs/heads/loose-checked-update $A &&
+ mark_packed_refs &&
+ git update-ref refs/heads/loose-checked-update $B $A &&
+ check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on verify of loose' '
+ git pack-refs --all &&
+ git update-ref refs/heads/loose-verify $A &&
+ mark_packed_refs &&
+ echo "verify refs/heads/loose-verify $A" | git update-ref --stdin &&
+ check_packed_refs_marked
+'
+
+test_expect_success 'leave packed-refs untouched on delete of loose' '
+ git pack-refs --all &&
+ git update-ref refs/heads/loose-delete $A &&
+ mark_packed_refs &&
+ git update-ref -d refs/heads/loose-delete &&
+ check_packed_refs_marked
+'
+
+test_done
rm -fr subdir
'
+test_expect_success 'rebase -i with exec allows git commands in subdirs' '
+ test_when_finished "rm -rf subdir" &&
+ test_when_finished "git rebase --abort ||:" &&
+ git checkout master &&
+ mkdir subdir && (cd subdir &&
+ set_fake_editor &&
+ FAKE_LINES="1 exec_cd_subdir_&&_git_rev-parse_--is-inside-work-tree" \
+ git rebase -i HEAD^
+ )
+'
+
test_expect_success 'rebase -i with the exec command checks tree cleanness' '
git checkout master &&
set_fake_editor &&
git submodule update &&
git checkout -q HEAD^ &&
git checkout -q master 2>actual &&
- test_i18ngrep "^warning: unable to rmdir submod:" actual &&
+ test_i18ngrep "^warning: unable to rmdir '\''submod'\'':" actual &&
git status -s submod >actual &&
echo "?? submod/" >expected &&
test_cmp expected actual &&
test_i18ncmp expect actual
'
-test_expect_success 'rm empty string should invoke warning' '
- git rm -rf "" 2>output &&
- test_i18ngrep "warning: empty strings" output
+test_expect_success 'rm empty string should fail' '
+ test_must_fail git rm -rf ""
'
test_done
test_i18ncmp expect.err actual.err
'
-test_expect_success 'git add empty string should invoke warning' '
- git add "" 2>output &&
- test_i18ngrep "warning: empty strings" output
+test_expect_success 'git add empty string should fail' '
+ test_must_fail git add ""
'
test_expect_success 'git add --chmod=[+-]x stages correctly' '
test_cmp expect actual
'
-test_expect_success 'move detection ignoring whitespace ' '
+test_expect_success 'set up whitespace tests' '
git reset --hard &&
- cat <<\EOF >lines.txt &&
-line 1
-line 2
-line 3
-line 4
-long line 5
-long line 6
-long line 7
-EOF
- git add lines.txt &&
- git commit -m "add poetry" &&
- cat <<\EOF >lines.txt &&
- long line 5
+ # Note that these lines have no leading or trailing whitespace.
+ cat <<-\EOF >lines.txt &&
+ line 1
+ line 2
+ line 3
+ line 4
+ line 5
long line 6
long line 7
-line 1
-line 2
-line 3
-line 4
-EOF
- test_config color.diff.oldMoved "magenta" &&
- test_config color.diff.newMoved "cyan" &&
+ long line 8
+ long line 9
+ EOF
+ git add lines.txt &&
+ git commit -m "add poetry" &&
+ git config color.diff.oldMoved "magenta" &&
+ git config color.diff.newMoved "cyan"
+'
+
+test_expect_success 'move detection ignoring whitespace ' '
+ q_to_tab <<-\EOF >lines.txt &&
+ Qlong line 6
+ Qlong line 7
+ Qlong line 8
+ Qchanged long line 9
+ line 1
+ line 2
+ line 3
+ line 4
+ line 5
+ EOF
git diff HEAD --no-renames --color-moved --color |
grep -v "index" |
test_decode_color >actual &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
<BOLD>+++ b/lines.txt<RESET>
- <CYAN>@@ -1,7 +1,7 @@<RESET>
- <GREEN>+<RESET> <GREEN>long line 5<RESET>
+ <CYAN>@@ -1,9 +1,9 @@<RESET>
<GREEN>+<RESET> <GREEN>long line 6<RESET>
<GREEN>+<RESET> <GREEN>long line 7<RESET>
+ <GREEN>+<RESET> <GREEN>long line 8<RESET>
+ <GREEN>+<RESET> <GREEN>changed long line 9<RESET>
line 1<RESET>
line 2<RESET>
line 3<RESET>
line 4<RESET>
- <RED>-long line 5<RESET>
+ line 5<RESET>
<RED>-long line 6<RESET>
<RED>-long line 7<RESET>
+ <RED>-long line 8<RESET>
+ <RED>-long line 9<RESET>
EOF
test_cmp expected actual &&
<BOLD>diff --git a/lines.txt b/lines.txt<RESET>
<BOLD>--- a/lines.txt<RESET>
<BOLD>+++ b/lines.txt<RESET>
- <CYAN>@@ -1,7 +1,7 @@<RESET>
- <CYAN>+<RESET> <CYAN>long line 5<RESET>
+ <CYAN>@@ -1,9 +1,9 @@<RESET>
<CYAN>+<RESET> <CYAN>long line 6<RESET>
<CYAN>+<RESET> <CYAN>long line 7<RESET>
+ <CYAN>+<RESET> <CYAN>long line 8<RESET>
+ <GREEN>+<RESET> <GREEN>changed long line 9<RESET>
+ line 1<RESET>
+ line 2<RESET>
+ line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ <MAGENTA>-long line 6<RESET>
+ <MAGENTA>-long line 7<RESET>
+ <MAGENTA>-long line 8<RESET>
+ <RED>-long line 9<RESET>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'move detection ignoring whitespace changes' '
+ git reset --hard &&
+ # Lines 6-8 have a space change, but 9 is new whitespace
+ q_to_tab <<-\EOF >lines.txt &&
+ longQline 6
+ longQline 7
+ longQline 8
+ long liQne 9
+ line 1
+ line 2
+ line 3
+ line 4
+ line 5
+ EOF
+
+ git diff HEAD --no-renames --color-moved --color |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,9 +1,9 @@<RESET>
+ <GREEN>+<RESET><GREEN>long line 6<RESET>
+ <GREEN>+<RESET><GREEN>long line 7<RESET>
+ <GREEN>+<RESET><GREEN>long line 8<RESET>
+ <GREEN>+<RESET><GREEN>long li ne 9<RESET>
+ line 1<RESET>
+ line 2<RESET>
+ line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ <RED>-long line 6<RESET>
+ <RED>-long line 7<RESET>
+ <RED>-long line 8<RESET>
+ <RED>-long line 9<RESET>
+ EOF
+ test_cmp expected actual &&
+
+ git diff HEAD --no-renames -b --color-moved --color |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,9 +1,9 @@<RESET>
+ <CYAN>+<RESET><CYAN>long line 6<RESET>
+ <CYAN>+<RESET><CYAN>long line 7<RESET>
+ <CYAN>+<RESET><CYAN>long line 8<RESET>
+ <GREEN>+<RESET><GREEN>long li ne 9<RESET>
+ line 1<RESET>
+ line 2<RESET>
+ line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ <MAGENTA>-long line 6<RESET>
+ <MAGENTA>-long line 7<RESET>
+ <MAGENTA>-long line 8<RESET>
+ <RED>-long line 9<RESET>
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'move detection ignoring whitespace at eol' '
+ git reset --hard &&
+ # Lines 6-9 have new eol whitespace, but 9 also has it in the middle
+ q_to_tab <<-\EOF >lines.txt &&
+ long line 6Q
+ long line 7Q
+ long line 8Q
+ longQline 9Q
+ line 1
+ line 2
+ line 3
+ line 4
+ line 5
+ EOF
+
+ # avoid cluttering the output with complaints about our eol whitespace
+ test_config core.whitespace -blank-at-eol &&
+
+ git diff HEAD --no-renames --color-moved --color |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,9 +1,9 @@<RESET>
+ <GREEN>+<RESET><GREEN>long line 6 <RESET>
+ <GREEN>+<RESET><GREEN>long line 7 <RESET>
+ <GREEN>+<RESET><GREEN>long line 8 <RESET>
+ <GREEN>+<RESET><GREEN>long line 9 <RESET>
+ line 1<RESET>
+ line 2<RESET>
+ line 3<RESET>
+ line 4<RESET>
+ line 5<RESET>
+ <RED>-long line 6<RESET>
+ <RED>-long line 7<RESET>
+ <RED>-long line 8<RESET>
+ <RED>-long line 9<RESET>
+ EOF
+ test_cmp expected actual &&
+
+ git diff HEAD --no-renames --ignore-space-at-eol --color-moved --color |
+ grep -v "index" |
+ test_decode_color >actual &&
+ cat <<-\EOF >expected &&
+ <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+ <BOLD>--- a/lines.txt<RESET>
+ <BOLD>+++ b/lines.txt<RESET>
+ <CYAN>@@ -1,9 +1,9 @@<RESET>
+ <CYAN>+<RESET><CYAN>long line 6 <RESET>
+ <CYAN>+<RESET><CYAN>long line 7 <RESET>
+ <CYAN>+<RESET><CYAN>long line 8 <RESET>
+ <GREEN>+<RESET><GREEN>long line 9 <RESET>
line 1<RESET>
line 2<RESET>
line 3<RESET>
line 4<RESET>
- <MAGENTA>-long line 5<RESET>
+ line 5<RESET>
<MAGENTA>-long line 6<RESET>
<MAGENTA>-long line 7<RESET>
+ <MAGENTA>-long line 8<RESET>
+ <RED>-long line 9<RESET>
EOF
test_cmp expected actual
'
+test_expect_success 'clean up whitespace-test colors' '
+ git config --unset color.diff.oldMoved &&
+ git config --unset color.diff.newMoved
+'
+
test_expect_success '--color-moved block at end of diff output respects MIN_ALNUM_COUNT' '
git reset --hard &&
>bar &&
test_cmp expect decoded_actual
'
-test_expect_success 'move detection with whitespace changes' '
- test_when_finished "git reset --hard" &&
- test_seq 10 >test &&
- git add test &&
- sed s/3/42/ <test >test.tmp &&
- mv test.tmp test &&
- git -c diff.colormoved diff --ignore-space-change -- test
-'
-
test_done
)
'
+test_expect_success 'git pull does not add a sign-off line' '
+ test_when_finished "rm -fr src dst actual" &&
+ git init src &&
+ test_commit -C src one &&
+ git clone src dst &&
+ test_commit -C src two &&
+ git -C dst pull --no-ff &&
+ git -C dst show -s --pretty="format:%(trailers)" HEAD >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'git pull --no-signoff does not add sign-off line' '
+ test_when_finished "rm -fr src dst actual" &&
+ git init src &&
+ test_commit -C src one &&
+ git clone src dst &&
+ test_commit -C src two &&
+ git -C dst pull --no-signoff --no-ff &&
+ git -C dst show -s --pretty="format:%(trailers)" HEAD >actual &&
+ test_must_be_empty actual
+'
+
+test_expect_success 'git pull --signoff add a sign-off line' '
+ test_when_finished "rm -fr src dst expected actual" &&
+ echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected &&
+ git init src &&
+ test_commit -C src one &&
+ git clone src dst &&
+ test_commit -C src two &&
+ git -C dst pull --signoff --no-ff &&
+ git -C dst show -s --pretty="format:%(trailers)" HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'git pull --no-signoff flag cancels --signoff flag' '
+ test_when_finished "rm -fr src dst actual" &&
+ git init src &&
+ test_commit -C src one &&
+ git clone src dst &&
+ test_commit -C src two &&
+ git -C dst pull --signoff --no-signoff --no-ff &&
+ git -C dst show -s --pretty="format:%(trailers)" HEAD >actual &&
+ test_must_be_empty actual
+'
+
test_done
git fetch >../actual.out 2>../actual.err
) &&
! test -s actual.out &&
- test_i18ncmp expect.err actual.err
+ test_i18ncmp expect.err actual.err &&
+ (
+ cd submodule &&
+ git checkout -q master
+ )
+'
+
+test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .gitmodule entry" '
+ (
+ cd downstream &&
+ git fetch --recurse-submodules
+ ) &&
+ add_upstream_commit &&
+ head1=$(git rev-parse --short HEAD) &&
+ git add submodule &&
+ git rm .gitmodules &&
+ git commit -m "new submodule without .gitmodules" &&
+ printf "" >expect.out &&
+ head2=$(git rev-parse --short HEAD) &&
+ echo "From $pwd/." >expect.err.2 &&
+ echo " $head1..$head2 master -> origin/master" >>expect.err.2 &&
+ head -3 expect.err >>expect.err.2 &&
+ (
+ cd downstream &&
+ rm .gitmodules &&
+ git config fetch.recurseSubmodules on-demand &&
+ # fake submodule configuration to avoid skipping submodule handling
+ git config -f .gitmodules submodule.fake.path fake &&
+ git config -f .gitmodules submodule.fake.url fakeurl &&
+ git add .gitmodules &&
+ git config --unset submodule.submodule.url &&
+ git fetch >../actual.out 2>../actual.err &&
+ # cleanup
+ git config --unset fetch.recurseSubmodules &&
+ git reset --hard
+ ) &&
+ test_i18ncmp expect.out actual.out &&
+ test_i18ncmp expect.err.2 actual.err &&
+ git checkout HEAD^ -- .gitmodules &&
+ git add .gitmodules &&
+ git commit -m "new submodule restored .gitmodules"
'
test_expect_success 'fetching submodules respects parallel settings' '
test_must_fail git -C dst fetch --recurse-submodules
'
+test_expect_success "fetch new commits when submodule got renamed" '
+ git clone . downstream_rename &&
+ (
+ cd downstream_rename &&
+ git submodule update --init &&
+# NEEDSWORK: we omitted --recursive for the submodule update here since
+# that does not work. See test 7001 for mv "moving nested submodules"
+# for details. Once that is fixed we should add the --recursive option
+# here.
+ git checkout -b rename &&
+ git mv submodule submodule_renamed &&
+ (
+ cd submodule_renamed &&
+ git checkout -b rename_sub &&
+ echo a >a &&
+ git add a &&
+ git commit -ma &&
+ git push origin rename_sub &&
+ git rev-parse HEAD >../../expect
+ ) &&
+ git add submodule_renamed &&
+ git commit -m "update renamed submodule" &&
+ git push origin rename
+ ) &&
+ (
+ cd downstream &&
+ git fetch --recurse-submodules=on-demand &&
+ (
+ cd submodule &&
+ git rev-parse origin/rename_sub >../../actual
+ )
+ ) &&
+ test_cmp expect actual
+'
+
test_done
test_cmp expect parent_upstream/.git/hooks/post-receive.push_options
'
+test_expect_success 'default push option' '
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions true &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ git -c push.pushOption=default push up master
+ ) &&
+ test_refs master master &&
+ echo "default" >expect &&
+ test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+ test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'two default push options' '
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions true &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ git -c push.pushOption=default1 -c push.pushOption=default2 push up master
+ ) &&
+ test_refs master master &&
+ printf "default1\ndefault2\n" >expect &&
+ test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+ test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'push option from command line overrides from-config push option' '
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions true &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ git -c push.pushOption=default push --push-option=manual up master
+ ) &&
+ test_refs master master &&
+ echo "manual" >expect &&
+ test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+ test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'empty value of push.pushOption in config clears the list' '
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions true &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ git -c push.pushOption=default1 -c push.pushOption= -c push.pushOption=default2 push up master
+ ) &&
+ test_refs master master &&
+ echo "default2" >expect &&
+ test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+ test_cmp expect upstream/.git/hooks/post-receive.push_options
+'
+
+test_expect_success 'invalid push option in config' '
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions true &&
+ (
+ cd workbench &&
+ test_commit one &&
+ git push --mirror up &&
+ test_commit two &&
+ test_must_fail git -c push.pushOption push up master
+ ) &&
+ test_refs master HEAD@{1}
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
test_description='various Windows-only path tests'
. ./test-lib.sh
-if ! test_have_prereq MINGW; then
+if test_have_prereq CYGWIN
+then
+ alias winpwd='cygpath -aw .'
+elif test_have_prereq MINGW
+then
+ alias winpwd=pwd
+else
skip_all='skipping Windows-only path tests'
test_done
fi
-UNCPATH="$(pwd)"
+UNCPATH="$(winpwd)"
case "$UNCPATH" in
[A-Z]:*)
# Use administrative share e.g. \\localhost\C$\git-sdk-64\usr\src\git
test "$rev" = "$(git rev-parse --verify refs/heads/to-push)"
'
-test_expect_success 'remote nick cannot contain backslashes' '
- BACKSLASHED="$(pwd | tr / \\\\)" &&
+test_expect_success MINGW 'remote nick cannot contain backslashes' '
+ BACKSLASHED="$(winpwd | tr / \\\\)" &&
git ls-remote "$BACKSLASHED" >out 2>err &&
test_i18ngrep ! "unable to access" err
'
setup_ssh_wrapper () {
test_expect_success 'setup ssh wrapper' '
+ rm -f "$TRASH_DIRECTORY/ssh-wrapper$X" &&
cp "$GIT_BUILD_DIR/t/helper/test-fake-ssh$X" \
"$TRASH_DIRECTORY/ssh-wrapper$X" &&
GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper$X" &&
}
copy_ssh_wrapper_as () {
+ rm -f "${1%$X}$X" &&
cp "$TRASH_DIRECTORY/ssh-wrapper$X" "${1%$X}$X" &&
GIT_SSH="${1%$X}$X" &&
export GIT_SSH
test_cmp expected actual
'
+test_expect_success 'git bisect reset cleans bisection state properly' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect reset &&
+ test -z "$(git for-each-ref "refs/bisect/*")" &&
+ test_path_is_missing "$GIT_DIR/BISECT_EXPECTED_REV" &&
+ test_path_is_missing "$GIT_DIR/BISECT_ANCESTORS_OK" &&
+ test_path_is_missing "$GIT_DIR/BISECT_LOG" &&
+ test_path_is_missing "$GIT_DIR/BISECT_RUN" &&
+ test_path_is_missing "$GIT_DIR/BISECT_TERMS" &&
+ test_path_is_missing "$GIT_DIR/head-name" &&
+ test_path_is_missing "$GIT_DIR/BISECT_HEAD" &&
+ test_path_is_missing "$GIT_DIR/BISECT_START"
+'
+
test_done
git mv sub sub2 &&
git commit -m "moved sub to sub2" &&
git checkout -q HEAD^ 2>actual &&
- test_i18ngrep "^warning: unable to rmdir sub2:" actual &&
+ test_i18ngrep "^warning: unable to rmdir '\''sub2'\'':" actual &&
git status -s sub2 >actual &&
echo "?? sub2/" >expected &&
test_cmp expected actual &&
test_cmp expect actual
'
+test_expect_success TTY 'git tag with auto-columns ' '
+ test_commit one &&
+ test_commit two &&
+ test_commit three &&
+ test_commit four &&
+ test_commit five &&
+ cat >expect <<-\EOF &&
+ initial one two three four five
+ EOF
+ test_terminal env PAGER="cat >actual" COLUMNS=80 \
+ git -c column.ui=auto tag --sort=authordate &&
+ test_cmp expect actual
+'
+
test_done
test_cmp expected actual
'
+cat >expected <<\EOF
+!! tracked/submodule/
+EOF
+
+test_expect_success 'status ignores submodule in excluded directory' '
+ git init tracked/submodule &&
+ test_commit -C tracked/submodule initial &&
+ git status --porcelain --ignored -u tracked/submodule >actual &&
+ test_cmp expected actual
+'
+
test_done
--- /dev/null
+#!/bin/sh
+
+test_description='ignored hook warning'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ hookdir="$(git rev-parse --git-dir)/hooks" &&
+ hook="$hookdir/pre-commit" &&
+ mkdir -p "$hookdir" &&
+ write_script "$hook" <<-\EOF
+ exit 0
+ EOF
+'
+
+test_expect_success 'no warning if hook is not ignored' '
+ git commit --allow-empty -m "more" 2>message &&
+ test_i18ngrep ! -e "hook was ignored" message
+'
+
+test_expect_success POSIXPERM 'warning if hook is ignored' '
+ chmod -x "$hook" &&
+ git commit --allow-empty -m "even more" 2>message &&
+ test_i18ngrep -e "hook was ignored" message
+'
+
+test_expect_success POSIXPERM 'no warning if advice.ignoredHook set to false' '
+ test_config advice.ignoredHook false &&
+ chmod -x "$hook" &&
+ git commit --allow-empty -m "even more" 2>message &&
+ test_i18ngrep ! -e "hook was ignored" message
+'
+
+test_expect_success 'no warning if unset advice.ignoredHook and hook removed' '
+ rm -f "$hook" &&
+ test_unconfig advice.ignoredHook &&
+ git commit --allow-empty -m "even more" 2>message &&
+ test_i18ngrep ! -e "hook was ignored" message
+'
+
+test_done
--- /dev/null
+#!/bin/sh
+
+test_description='git status ignored modes'
+
+. ./test-lib.sh
+
+test_expect_success 'setup initial commit and ignore file' '
+ cat >.gitignore <<-\EOF &&
+ *.ign
+ ignored_dir/
+ !*.unignore
+ EOF
+ git add . &&
+ git commit -m "Initial commit"
+'
+
+test_expect_success 'Verify behavior of status on directories with ignored files' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! dir/ignored/ignored_1.ign
+ ! dir/ignored/ignored_2.ign
+ ! ignored/ignored_1.ign
+ ! ignored/ignored_2.ign
+ EOF
+
+ mkdir -p ignored dir/ignored &&
+ touch ignored/ignored_1.ign ignored/ignored_2.ign \
+ dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
+
+ git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status behavior on directory with tracked & ignored files' '
+ test_when_finished "git clean -fdx && git reset HEAD~1 --hard" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! dir/tracked_ignored/ignored_1.ign
+ ! dir/tracked_ignored/ignored_2.ign
+ ! tracked_ignored/ignored_1.ign
+ ! tracked_ignored/ignored_2.ign
+ EOF
+
+ mkdir -p tracked_ignored dir/tracked_ignored &&
+ touch tracked_ignored/tracked_1 tracked_ignored/tracked_2 \
+ tracked_ignored/ignored_1.ign tracked_ignored/ignored_2.ign \
+ dir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 \
+ dir/tracked_ignored/ignored_1.ign dir/tracked_ignored/ignored_2.ign &&
+
+ git add tracked_ignored/tracked_1 tracked_ignored/tracked_2 \
+ dir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 &&
+ git commit -m "commit tracked files" &&
+
+ git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status behavior on directory with untracked and ignored files' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? dir/untracked_ignored/untracked_1
+ ? dir/untracked_ignored/untracked_2
+ ? expect
+ ? output
+ ? untracked_ignored/untracked_1
+ ? untracked_ignored/untracked_2
+ ! dir/untracked_ignored/ignored_1.ign
+ ! dir/untracked_ignored/ignored_2.ign
+ ! untracked_ignored/ignored_1.ign
+ ! untracked_ignored/ignored_2.ign
+ EOF
+
+ mkdir -p untracked_ignored dir/untracked_ignored &&
+ touch untracked_ignored/untracked_1 untracked_ignored/untracked_2 \
+ untracked_ignored/ignored_1.ign untracked_ignored/ignored_2.ign \
+ dir/untracked_ignored/untracked_1 dir/untracked_ignored/untracked_2 \
+ dir/untracked_ignored/ignored_1.ign dir/untracked_ignored/ignored_2.ign &&
+
+ git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status matching ignored files on ignored directory' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! ignored_dir/
+ EOF
+
+ mkdir ignored_dir &&
+ touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+ ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign &&
+
+ git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status behavior on ignored directory containing tracked file' '
+ test_when_finished "git clean -fdx && git reset HEAD~1 --hard" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! ignored_dir/ignored_1
+ ! ignored_dir/ignored_1.ign
+ ! ignored_dir/ignored_2
+ ! ignored_dir/ignored_2.ign
+ EOF
+
+ mkdir ignored_dir &&
+ touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+ ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \
+ ignored_dir/tracked &&
+ git add -f ignored_dir/tracked &&
+ git commit -m "Force add file in ignored directory" &&
+ git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ? untracked_dir/
+ ! ignored_dir/
+ ! ignored_files/ignored_1.ign
+ ! ignored_files/ignored_2.ign
+ EOF
+
+ mkdir ignored_dir ignored_files untracked_dir &&
+ touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+ ignored_files/ignored_1.ign ignored_files/ignored_2.ign \
+ untracked_dir/untracked &&
+ git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify matching ignored files with --untracked-files=normal' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ? untracked_dir/
+ ! ignored_dir/
+ ! ignored_files/ignored_1.ign
+ ! ignored_files/ignored_2.ign
+ EOF
+
+ mkdir ignored_dir ignored_files untracked_dir &&
+ touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+ ignored_files/ignored_1.ign ignored_files/ignored_2.ign \
+ untracked_dir/untracked &&
+ git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify status behavior on ignored directory containing tracked file' '
+ test_when_finished "git clean -fdx && git reset HEAD~1 --hard" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! ignored_dir/ignored_1
+ ! ignored_dir/ignored_1.ign
+ ! ignored_dir/ignored_2
+ ! ignored_dir/ignored_2.ign
+ EOF
+
+ mkdir ignored_dir &&
+ touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
+ ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \
+ ignored_dir/tracked &&
+ git add -f ignored_dir/tracked &&
+ git commit -m "Force add file in ignored directory" &&
+ git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify behavior of status with --ignored=no' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ EOF
+
+ mkdir -p ignored dir/ignored &&
+ touch ignored/ignored_1.ign ignored/ignored_2.ign \
+ dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
+
+ git status --porcelain=v2 --ignored=no --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=all' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! dir/ignored/ignored_1.ign
+ ! dir/ignored/ignored_2.ign
+ ! ignored/ignored_1.ign
+ ! ignored/ignored_2.ign
+ EOF
+
+ mkdir -p ignored dir/ignored &&
+ touch ignored/ignored_1.ign ignored/ignored_2.ign \
+ dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
+
+ git status --porcelain=v2 --ignored=traditional --untracked-files=all >output &&
+ test_i18ncmp expect output
+'
+
+test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=normal' '
+ test_when_finished "git clean -fdx" &&
+ cat >expect <<-\EOF &&
+ ? expect
+ ? output
+ ! dir/
+ ! ignored/
+ EOF
+
+ mkdir -p ignored dir/ignored &&
+ touch ignored/ignored_1.ign ignored/ignored_2.ign \
+ dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
+
+ git status --porcelain=v2 --ignored=traditional --untracked-files=normal >output &&
+ test_i18ncmp expect output
+'
+
+test_done
--conflict=
--orphan Z
--patch Z
+ --detach Z
+ --ignore-skip-worktree-bits Z
+ --recurse-submodules Z
+ --no-recurse-submodules Z
EOF
'
* `create_tempfile()` returns an allocated tempfile on success or NULL
* on failure. On errors, `errno` describes the reason for failure.
*
- * `delete_tempfile()`, `rename_tempfile()`, and `close_tempfile_gently()`
- * return 0 on success. On failure they set `errno` appropriately and return
- * -1. `delete` and `rename` (but not `close`) do their best to delete the
- * temporary file before returning.
+ * `rename_tempfile()` and `close_tempfile_gently()` return 0 on success.
+ * On failure they set `errno` appropriately and return -1.
+ * `delete_tempfile()` and `rename` (but not `close`) do their best to
+ * delete the temporary file before returning.
*/
struct tempfile {
mode = 0;
}
- if (DIFF_OPT_TST(opt, RECURSIVE) && isdir) {
+ if (opt->flags.recursive && isdir) {
recurse = 1;
- emitthis = DIFF_OPT_TST(opt, TREE_IN_RECURSIVE);
+ emitthis = opt->flags.tree_in_recursive;
}
if (emitthis) {
ttree = fill_tree_descriptor(&t, oid);
/* Enable recursion indefinitely */
- opt->pathspec.recursive = DIFF_OPT_TST(opt, RECURSIVE);
+ opt->pathspec.recursive = opt->flags.recursive;
for (;;) {
int imin, cmp;
/* t = p[imin] */
if (cmp == 0) {
/* are either pi > p[imin] or diff(t,pi) != ø ? */
- if (!DIFF_OPT_TST(opt, FIND_COPIES_HARDER)) {
+ if (!opt->flags.find_copies_harder) {
for (i = 0; i < nparent; ++i) {
/* p[i] > p[imin] */
if (tp[i].entry.mode & S_IFXMIN_NEQ)
/* t > p[imin] */
else {
/* ∀i pi=p[imin] -> D += "-p[imin]" */
- if (!DIFF_OPT_TST(opt, FIND_COPIES_HARDER)) {
+ if (!opt->flags.find_copies_harder) {
for (i = 0; i < nparent; ++i)
if (tp[i].entry.mode & S_IFXMIN_NEQ)
goto skip_emit_tp;
q->nr = 0;
diff_setup(&diff_opts);
- DIFF_OPT_SET(&diff_opts, RECURSIVE);
- DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
+ diff_opts.flags.recursive = 1;
+ diff_opts.flags.find_copies_harder = 1;
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
diff_opts.single_follow = opt->pathspec.items[0].match;
diff_opts.break_opt = opt->break_opt;
strbuf_addstr(&base, base_str);
retval = ll_diff_tree_oid(old_oid, new_oid, &base, opt);
- if (!*base_str && DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename())
+ if (!*base_str && opt->flags.follow_renames && diff_might_be_rename())
try_to_follow_renames(old_oid, new_oid, &base, opt);
strbuf_release(&base);
refs = get_worktree_ref_store(wt);
symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
NULL, &flags);
- if ((flags & REF_ISSYMREF) && !strcmp(symref_target, target)) {
+ if ((flags & REF_ISSYMREF) &&
+ symref_target && !strcmp(symref_target, target)) {
existing = wt;
break;
}
if (!rc || errno == ENOENT)
return 0;
err = errno;
- warning_errno("unable to %s %s", op, file);
+ warning_errno("unable to %s '%s'", op, file);
errno = err;
return rc;
}
if (!rc || errno == ENOENT)
return 0;
- strbuf_addf(err, "unable to unlink %s: %s",
+ strbuf_addf(err, "unable to unlink '%s': %s",
file, strerror(errno));
return -1;
}
{
int fd = xopen(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (write_in_full(fd, buf, len) < 0)
- die_errno(_("could not write to %s"), path);
+ die_errno(_("could not write to '%s'"), path);
if (close(fd))
- die_errno(_("could not close %s"), path);
+ die_errno(_("could not close '%s'"), path);
}
void write_file(const char *path, const char *fmt, ...)
init_revisions(&rev, NULL);
setup_revisions(0, NULL, &rev, NULL);
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
- DIFF_OPT_SET(&rev.diffopt, DIRTY_SUBMODULES);
+ rev.diffopt.flags.dirty_submodules = 1;
rev.diffopt.ita_invisible_in_index = 1;
if (!s->show_untracked_files)
- DIFF_OPT_SET(&rev.diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+ rev.diffopt.flags.ignore_untracked_in_submodules = 1;
if (s->ignore_submodule_arg) {
- DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
+ rev.diffopt.flags.override_submodule_config = 1;
handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
}
rev.diffopt.format_callback = wt_status_collect_changed_cb;
opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
setup_revisions(0, NULL, &rev, &opt);
- DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
+ rev.diffopt.flags.override_submodule_config = 1;
rev.diffopt.ita_invisible_in_index = 1;
if (s->ignore_submodule_arg) {
handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
dir.flags |=
DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
- if (s->show_ignored_files)
+ if (s->show_ignored_mode) {
dir.flags |= DIR_SHOW_IGNORED_TOO;
- else
+
+ if (s->show_ignored_mode == SHOW_MATCHING_IGNORED)
+ dir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;
+ } else {
dir.untracked = the_index.untracked;
+ }
+
setup_standard_excludes(&dir);
fill_directory(&dir, &the_index, &s->pathspec);
const char *c = color(WT_STATUS_HEADER, s);
init_revisions(&rev, NULL);
- DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
+ rev.diffopt.flags.allow_textconv = 1;
rev.diffopt.ita_invisible_in_index = 1;
memset(&opt, 0, sizeof(opt));
}
if (s->show_untracked_files) {
wt_longstatus_print_other(s, &s->untracked, _("Untracked files"), "add");
- if (s->show_ignored_files)
+ if (s->show_ignored_mode)
wt_longstatus_print_other(s, &s->ignored, _("Ignored files"), "add -f");
if (advice_status_u_option && 2000 < s->untracked_in_ms) {
status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
init_revisions(&rev_info, NULL);
if (ignore_submodules)
- DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);
- DIFF_OPT_SET(&rev_info.diffopt, QUICK);
+ rev_info.diffopt.flags.ignore_submodules = 1;
+ rev_info.diffopt.flags.quick = 1;
diff_setup_done(&rev_info.diffopt);
result = run_diff_files(&rev_info, 0);
return diff_result_code(&rev_info.diffopt, result);
init_revisions(&rev_info, NULL);
if (ignore_submodules)
- DIFF_OPT_SET(&rev_info.diffopt, IGNORE_SUBMODULES);
- DIFF_OPT_SET(&rev_info.diffopt, QUICK);
+ rev_info.diffopt.flags.ignore_submodules = 1;
+ rev_info.diffopt.flags.quick = 1;
add_head_to_pending(&rev_info);
diff_setup_done(&rev_info.diffopt);
result = run_diff_index(&rev_info, 1);
*/
int require_clean_work_tree(const char *action, const char *hint, int ignore_submodules, int gently)
{
- struct lock_file *lock_file = xcalloc(1, sizeof(*lock_file));
+ struct lock_file lock_file = LOCK_INIT;
int err = 0, fd;
- fd = hold_locked_index(lock_file, 0);
+ fd = hold_locked_index(&lock_file, 0);
refresh_cache(REFRESH_QUIET);
if (0 <= fd)
- update_index_if_able(&the_index, lock_file);
- rollback_lock_file(lock_file);
+ update_index_if_able(&the_index, &lock_file);
+ rollback_lock_file(&lock_file);
if (has_unstaged_changes(ignore_submodules)) {
/* TRANSLATORS: the action is e.g. "pull with rebase" */
SHOW_ALL_UNTRACKED_FILES
};
+enum show_ignored_type {
+ SHOW_NO_IGNORED,
+ SHOW_TRADITIONAL_IGNORED,
+ SHOW_MATCHING_IGNORED,
+};
+
/* from where does this commit originate */
enum commit_whence {
FROM_COMMIT, /* normal */
int display_comment_prefix;
int relative_paths;
int submodule_summary;
- int show_ignored_files;
+ enum show_ignored_type show_ignored_mode;
enum untracked_status_type show_untracked_files;
const char *ignore_submodule_arg;
char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];
#include "xdiff/xdiffi.h"
#include "xdiff/xemit.h"
#include "xdiff/xmacros.h"
+#include "xdiff/xutils.h"
struct xdiff_emit_state {
xdiff_emit_consume_fn consume;
}
}
+unsigned long xdiff_hash_string(const char *s, size_t len, long flags)
+{
+ return xdl_hash_record(&s, s + len, flags);
+}
+
+int xdiff_compare_lines(const char *l1, long s1,
+ const char *l2, long s2, long flags)
+{
+ return xdl_recmatch(l1, s1, l2, s2, flags);
+}
+
int git_xmerge_style = -1;
int git_xmerge_config(const char *var, const char *value, void *cb)
extern int git_xmerge_config(const char *var, const char *value, void *cb);
extern int git_xmerge_style;
+/*
+ * Compare the strings l1 with l2 which are of size s1 and s2 respectively.
+ * Returns 1 if the strings are deemed equal, 0 otherwise.
+ * The `flags` given as XDF_WHITESPACE_FLAGS determine how white spaces
+ * are treated for the comparision.
+ */
+extern int xdiff_compare_lines(const char *l1, long s1,
+ const char *l2, long s2, long flags);
+
+/*
+ * Returns a hash of the string s of length len.
+ * The `flags` given as XDF_WHITESPACE_FLAGS determine how white spaces
+ * are treated for the hash.
+ */
+extern unsigned long xdiff_hash_string(const char *s, size_t len, long flags);
+
#endif