Compare commits
67 Commits
v2.25.0-rc
...
v2.25.0
Author | SHA1 | Date | |
---|---|---|---|
d0654dc308 | |||
b4615e40a8 | |||
4d924528d8 | |||
ddc12c429b | |||
e23b95e75b | |||
1cf4836865 | |||
d78a1968c5 | |||
a20ae3ee29 | |||
49e268e23e | |||
63a5650a49 | |||
75449c1b39 | |||
6b6a9803fb | |||
3901d2c6bd | |||
7a6a90c6ec | |||
1f5f3ffe5c | |||
a4e4140ac9 | |||
202f68b252 | |||
7e65f8638e | |||
c5dc20638b | |||
fe47c9cb5f | |||
a1087c9367 | |||
0d251c3291 | |||
042ed3e048 | |||
0f1930cd1b | |||
037f067587 | |||
f25f04edca | |||
c20d4fd44a | |||
a578ef9e63 | |||
c4117fcb97 | |||
556f0258df | |||
5814d44d9b | |||
7fdc5f296f | |||
f8740c586b | |||
4e2c4c0d4f | |||
421c0ffb02 | |||
757ff352bd | |||
0d2116c644 | |||
9d48668cd5 | |||
3a05aacddd | |||
4c5081614c | |||
5bb457409c | |||
63020f175f | |||
224c7d70fa | |||
763a59e71c | |||
44143583b7 | |||
d6a6263f5f | |||
13185fd241 | |||
786f4d2405 | |||
4fd683b6a3 | |||
de11951b03 | |||
0d3ce942b0 | |||
578c793731 | |||
173fff68da | |||
9e341f62ca | |||
4e61b2214d | |||
ace0f86c7f | |||
2be45868a8 | |||
280738c36e | |||
23cbe427c4 | |||
1e1ccbfdd3 | |||
d32e065a91 | |||
39acfa3d22 | |||
2763530048 | |||
fa38ab68b0 | |||
d9c6469f38 | |||
29a9366052 | |||
9917eca794 |
@ -53,9 +53,6 @@ UI, Workflows & Features
|
|||||||
or a named file, instead of taking it as the command line
|
or a named file, instead of taking it as the command line
|
||||||
arguments, with the "--pathspec-from-file" option.
|
arguments, with the "--pathspec-from-file" option.
|
||||||
|
|
||||||
* "git rebase -i" learned a few options that are known by "git
|
|
||||||
rebase" proper.
|
|
||||||
|
|
||||||
* "git submodule" learned a subcommand "set-url".
|
* "git submodule" learned a subcommand "set-url".
|
||||||
|
|
||||||
* "git log" family learned "--pretty=reference" that gives the name
|
* "git log" family learned "--pretty=reference" that gives the name
|
||||||
@ -72,6 +69,9 @@ UI, Workflows & Features
|
|||||||
|
|
||||||
* Miscellaneous small UX improvements on "git-p4".
|
* Miscellaneous small UX improvements on "git-p4".
|
||||||
|
|
||||||
|
* "git sparse-checkout list" subcommand learned to give its output in
|
||||||
|
a more concise form when the "cone" mode is in effect.
|
||||||
|
|
||||||
|
|
||||||
Performance, Internal Implementation, Development Support etc.
|
Performance, Internal Implementation, Development Support etc.
|
||||||
|
|
||||||
@ -143,8 +143,6 @@ Performance, Internal Implementation, Development Support etc.
|
|||||||
* The code has been made to avoid gmtime() and localtime() and prefer
|
* The code has been made to avoid gmtime() and localtime() and prefer
|
||||||
their reentrant counterparts.
|
their reentrant counterparts.
|
||||||
|
|
||||||
* The effort to reimplement "git add -i" in C continues.
|
|
||||||
|
|
||||||
* In a repository with many packfiles, the cost of the procedure that
|
* In a repository with many packfiles, the cost of the procedure that
|
||||||
avoids registering the same packfile twice was unnecessarily high
|
avoids registering the same packfile twice was unnecessarily high
|
||||||
by using an inefficient search algorithm, which has been corrected.
|
by using an inefficient search algorithm, which has been corrected.
|
||||||
@ -315,6 +313,18 @@ Fixes since v2.24
|
|||||||
in C, which has been corrected.
|
in C, which has been corrected.
|
||||||
(merge 4fe7e43c53 en/rebase-signoff-fix later to maint).
|
(merge 4fe7e43c53 en/rebase-signoff-fix later to maint).
|
||||||
|
|
||||||
|
* An earlier update to Git for Windows declared that a tree object is
|
||||||
|
invalid if it has a path component with backslash in it, which was
|
||||||
|
overly strict, which has been corrected. The only protection the
|
||||||
|
Windows users need is to prevent such path (or any path that their
|
||||||
|
filesystem cannot check out) from entering the index.
|
||||||
|
(merge 224c7d70fa js/mingw-loosen-overstrict-tree-entry-checks later to maint).
|
||||||
|
|
||||||
|
* The code to write split commit-graph file(s) upon fetching computed
|
||||||
|
bogus value for the parameter used in splitting the resulting
|
||||||
|
files, which has been corrected.
|
||||||
|
(merge 63020f175f ds/commit-graph-set-size-mult later to maint).
|
||||||
|
|
||||||
* Other code cleanup, docfix, build fix, etc.
|
* Other code cleanup, docfix, build fix, etc.
|
||||||
(merge 80736d7c5e jc/am-show-current-patch-docfix later to maint).
|
(merge 80736d7c5e jc/am-show-current-patch-docfix later to maint).
|
||||||
(merge 8b656572ca sg/commit-graph-usage-fix later to maint).
|
(merge 8b656572ca sg/commit-graph-usage-fix later to maint).
|
||||||
@ -351,3 +361,10 @@ Fixes since v2.24
|
|||||||
(merge df5be01669 ja/doc-markup-cleanup later to maint).
|
(merge df5be01669 ja/doc-markup-cleanup later to maint).
|
||||||
(merge 7c5cea7242 mr/bisect-save-pointer-to-const-string later to maint).
|
(merge 7c5cea7242 mr/bisect-save-pointer-to-const-string later to maint).
|
||||||
(merge 20a67e8ce9 js/use-test-tool-on-path later to maint).
|
(merge 20a67e8ce9 js/use-test-tool-on-path later to maint).
|
||||||
|
(merge 4e61b2214d ew/packfile-syscall-optim later to maint).
|
||||||
|
(merge ace0f86c7f pb/clarify-line-log-doc later to maint).
|
||||||
|
(merge 763a59e71c en/merge-recursive-oid-eq-simplify later to maint).
|
||||||
|
(merge 4e2c4c0d4f do/gitweb-typofix-in-comments later to maint).
|
||||||
|
(merge 421c0ffb02 jb/doc-multi-pack-idx-fix later to maint).
|
||||||
|
(merge f8740c586b pm/am-in-body-header-doc-update later to maint).
|
||||||
|
(merge 5814d44d9b tm/doc-submodule-absorb-fix later to maint).
|
||||||
|
@ -107,7 +107,7 @@ advice.*::
|
|||||||
editor input from the user.
|
editor input from the user.
|
||||||
nestedTag::
|
nestedTag::
|
||||||
Advice shown if a user attempts to recursively tag a tag object.
|
Advice shown if a user attempts to recursively tag a tag object.
|
||||||
submoduleAlternateErrorStrategyDie:
|
submoduleAlternateErrorStrategyDie::
|
||||||
Advice shown when a submodule.alternateErrorStrategy option
|
Advice shown when a submodule.alternateErrorStrategy option
|
||||||
configured to "die" causes a fatal error.
|
configured to "die" causes a fatal error.
|
||||||
--
|
--
|
||||||
|
@ -190,8 +190,8 @@ the commit, after stripping common prefix "[PATCH <anything>]".
|
|||||||
The "Subject: " line is supposed to concisely describe what the
|
The "Subject: " line is supposed to concisely describe what the
|
||||||
commit is about in one line of text.
|
commit is about in one line of text.
|
||||||
|
|
||||||
"From: " and "Subject: " lines starting the body override the respective
|
"From: ", "Date: ", and "Subject: " lines starting the body override the
|
||||||
commit author name and title values taken from the headers.
|
respective commit author name and title values taken from the headers.
|
||||||
|
|
||||||
The commit message is formed by the title taken from the
|
The commit message is formed by the title taken from the
|
||||||
"Subject: ", a blank line and the body of the message up to
|
"Subject: ", a blank line and the body of the message up to
|
||||||
|
@ -76,8 +76,12 @@ produced by `--stat`, etc.
|
|||||||
(or the function name regex <funcname>) within the <file>. You may
|
(or the function name regex <funcname>) within the <file>. You may
|
||||||
not give any pathspec limiters. This is currently limited to
|
not give any pathspec limiters. This is currently limited to
|
||||||
a walk starting from a single revision, i.e., you may only
|
a walk starting from a single revision, i.e., you may only
|
||||||
give zero or one positive revision arguments.
|
give zero or one positive revision arguments, and
|
||||||
You can specify this option more than once.
|
<start> and <end> (or <funcname>) must exist in the starting revision.
|
||||||
|
You can specify this option more than once. Implies `--patch`.
|
||||||
|
Patch output can be suppressed using `--no-patch`, but other diff formats
|
||||||
|
(namely `--raw`, `--numstat`, `--shortstat`, `--dirstat`, `--summary`,
|
||||||
|
`--name-only`, `--name-status`, `--check`) are not currently implemented.
|
||||||
+
|
+
|
||||||
include::line-range-format.txt[]
|
include::line-range-format.txt[]
|
||||||
|
|
||||||
|
@ -393,31 +393,16 @@ your branch contains commits which were dropped, this option can be used
|
|||||||
with `--keep-base` in order to drop those commits from your branch.
|
with `--keep-base` in order to drop those commits from your branch.
|
||||||
|
|
||||||
--ignore-whitespace::
|
--ignore-whitespace::
|
||||||
Behaves differently depending on which backend is selected.
|
|
||||||
+
|
|
||||||
'am' backend: When applying a patch, ignore changes in whitespace in
|
|
||||||
context lines if necessary.
|
|
||||||
+
|
|
||||||
'interactive' backend: Treat lines with only whitespace changes as
|
|
||||||
unchanged for the sake of a three-way merge.
|
|
||||||
|
|
||||||
--whitespace=<option>::
|
--whitespace=<option>::
|
||||||
This flag is passed to the 'git apply' program
|
These flag are passed to the 'git apply' program
|
||||||
(see linkgit:git-apply[1]) that applies the patch.
|
(see linkgit:git-apply[1]) that applies the patch.
|
||||||
+
|
+
|
||||||
See also INCOMPATIBLE OPTIONS below.
|
See also INCOMPATIBLE OPTIONS below.
|
||||||
|
|
||||||
--committer-date-is-author-date::
|
--committer-date-is-author-date::
|
||||||
Instead of recording the time the rebased commits are
|
|
||||||
created as the committer date, reuse the author date
|
|
||||||
as the committer date. This implies --force-rebase.
|
|
||||||
|
|
||||||
--ignore-date::
|
--ignore-date::
|
||||||
--reset-author-date::
|
These flags are passed to 'git am' to easily change the dates
|
||||||
By default, the author date of the original commit is used
|
of the rebased commits (see linkgit:git-am[1]).
|
||||||
as the author date for the resulting commit. This option
|
|
||||||
tells Git to use the current timestamp instead and implies
|
|
||||||
`--force-rebase`.
|
|
||||||
+
|
+
|
||||||
See also INCOMPATIBLE OPTIONS below.
|
See also INCOMPATIBLE OPTIONS below.
|
||||||
|
|
||||||
@ -554,7 +539,10 @@ INCOMPATIBLE OPTIONS
|
|||||||
|
|
||||||
The following options:
|
The following options:
|
||||||
|
|
||||||
|
* --committer-date-is-author-date
|
||||||
|
* --ignore-date
|
||||||
* --whitespace
|
* --whitespace
|
||||||
|
* --ignore-whitespace
|
||||||
* -C
|
* -C
|
||||||
|
|
||||||
are incompatible with the following options:
|
are incompatible with the following options:
|
||||||
@ -577,9 +565,6 @@ In addition, the following pairs of options are incompatible:
|
|||||||
* --preserve-merges and --interactive
|
* --preserve-merges and --interactive
|
||||||
* --preserve-merges and --signoff
|
* --preserve-merges and --signoff
|
||||||
* --preserve-merges and --rebase-merges
|
* --preserve-merges and --rebase-merges
|
||||||
* --preserve-merges and --ignore-whitespace
|
|
||||||
* --preserve-merges and --committer-date-is-author-date
|
|
||||||
* --preserve-merges and --ignore-date
|
|
||||||
* --keep-base and --onto
|
* --keep-base and --onto
|
||||||
* --keep-base and --root
|
* --keep-base and --root
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ NAME
|
|||||||
----
|
----
|
||||||
git-sparse-checkout - Initialize and modify the sparse-checkout
|
git-sparse-checkout - Initialize and modify the sparse-checkout
|
||||||
configuration, which reduces the checkout to a set of paths
|
configuration, which reduces the checkout to a set of paths
|
||||||
given by a list of atterns.
|
given by a list of patterns.
|
||||||
|
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
@ -28,7 +28,7 @@ THE FUTURE.
|
|||||||
COMMANDS
|
COMMANDS
|
||||||
--------
|
--------
|
||||||
'list'::
|
'list'::
|
||||||
Provide a list of the contents in the sparse-checkout file.
|
Describe the patterns in the sparse-checkout file.
|
||||||
|
|
||||||
'init'::
|
'init'::
|
||||||
Enable the `core.sparseCheckout` setting. If the
|
Enable the `core.sparseCheckout` setting. If the
|
||||||
@ -150,11 +150,30 @@ expecting patterns of these types. Git will warn if the patterns do not match.
|
|||||||
If the patterns do match the expected format, then Git will use faster hash-
|
If the patterns do match the expected format, then Git will use faster hash-
|
||||||
based algorithms to compute inclusion in the sparse-checkout.
|
based algorithms to compute inclusion in the sparse-checkout.
|
||||||
|
|
||||||
|
In the cone mode case, the `git sparse-checkout list` subcommand will list the
|
||||||
|
directories that define the recursive patterns. For the example sparse-checkout
|
||||||
|
file above, the output is as follows:
|
||||||
|
|
||||||
|
--------------------------
|
||||||
|
$ git sparse-checkout list
|
||||||
|
A/B/C
|
||||||
|
--------------------------
|
||||||
|
|
||||||
If `core.ignoreCase=true`, then the pattern-matching algorithm will use a
|
If `core.ignoreCase=true`, then the pattern-matching algorithm will use a
|
||||||
case-insensitive check. This corrects for case mismatched filenames in the
|
case-insensitive check. This corrects for case mismatched filenames in the
|
||||||
'git sparse-checkout set' command to reflect the expected cone in the working
|
'git sparse-checkout set' command to reflect the expected cone in the working
|
||||||
directory.
|
directory.
|
||||||
|
|
||||||
|
|
||||||
|
SUBMODULES
|
||||||
|
----------
|
||||||
|
|
||||||
|
If your repository contains one or more submodules, then those submodules will
|
||||||
|
appear based on which you initialized with the `git submodule` command. If
|
||||||
|
your sparse-checkout patterns exclude an initialized submodule, then that
|
||||||
|
submodule will still appear in your working directory.
|
||||||
|
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
@ -248,7 +248,7 @@ registered submodules, and sync any nested submodules within.
|
|||||||
|
|
||||||
absorbgitdirs::
|
absorbgitdirs::
|
||||||
If a git directory of a submodule is inside the submodule,
|
If a git directory of a submodule is inside the submodule,
|
||||||
move the git directory of the submodule into its superprojects
|
move the git directory of the submodule into its superproject's
|
||||||
`$GIT_DIR/modules` path and then connect the git directory and
|
`$GIT_DIR/modules` path and then connect the git directory and
|
||||||
its working directory by setting the `core.worktree` and adding
|
its working directory by setting the `core.worktree` and adding
|
||||||
a .git file pointing to the git directory embedded in the
|
a .git file pointing to the git directory embedded in the
|
||||||
|
@ -105,8 +105,12 @@ linkgit:git-rev-list[1] for a complete list.
|
|||||||
(or the function name regex <funcname>) within the <file>. You may
|
(or the function name regex <funcname>) within the <file>. You may
|
||||||
not give any pathspec limiters. This is currently limited to
|
not give any pathspec limiters. This is currently limited to
|
||||||
a walk starting from a single revision, i.e., you may only
|
a walk starting from a single revision, i.e., you may only
|
||||||
give zero or one positive revision arguments.
|
give zero or one positive revision arguments, and
|
||||||
You can specify this option more than once.
|
<start> and <end> (or <funcname>) must exist in the starting revision.
|
||||||
|
You can specify this option more than once. Implies `--patch`.
|
||||||
|
Patch output can be suppressed using `--no-patch`, but other diff formats
|
||||||
|
(namely `--raw`, `--numstat`, `--shortstat`, `--dirstat`, `--summary`,
|
||||||
|
`--name-only`, `--name-status`, `--check`) are not currently implemented.
|
||||||
+
|
+
|
||||||
*Note:* gitk (unlike linkgit:git-log[1]) currently only understands
|
*Note:* gitk (unlike linkgit:git-log[1]) currently only understands
|
||||||
this option if you specify it "glued together" with its argument. Do
|
this option if you specify it "glued together" with its argument. Do
|
||||||
|
@ -36,7 +36,7 @@ Design Details
|
|||||||
directory of an alternate. It refers only to packfiles in that
|
directory of an alternate. It refers only to packfiles in that
|
||||||
same directory.
|
same directory.
|
||||||
|
|
||||||
- The pack.multiIndex config setting must be on to consume MIDX files.
|
- The core.multiPackIndex config setting must be on to consume MIDX files.
|
||||||
|
|
||||||
- The file format includes parameters for the object ID hash
|
- The file format includes parameters for the object ID hash
|
||||||
function, so a future change of hash algorithm does not require
|
function, so a future change of hash algorithm does not require
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
GVF=GIT-VERSION-FILE
|
GVF=GIT-VERSION-FILE
|
||||||
DEF_VER=v2.25.0-rc1
|
DEF_VER=v2.25.0
|
||||||
|
|
||||||
LF='
|
LF='
|
||||||
'
|
'
|
||||||
|
@ -1866,15 +1866,13 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
|||||||
(fetch_write_commit_graph < 0 &&
|
(fetch_write_commit_graph < 0 &&
|
||||||
the_repository->settings.fetch_write_commit_graph)) {
|
the_repository->settings.fetch_write_commit_graph)) {
|
||||||
int commit_graph_flags = COMMIT_GRAPH_WRITE_SPLIT;
|
int commit_graph_flags = COMMIT_GRAPH_WRITE_SPLIT;
|
||||||
struct split_commit_graph_opts split_opts;
|
|
||||||
memset(&split_opts, 0, sizeof(struct split_commit_graph_opts));
|
|
||||||
|
|
||||||
if (progress)
|
if (progress)
|
||||||
commit_graph_flags |= COMMIT_GRAPH_WRITE_PROGRESS;
|
commit_graph_flags |= COMMIT_GRAPH_WRITE_PROGRESS;
|
||||||
|
|
||||||
write_commit_graph_reachable(get_object_directory(),
|
write_commit_graph_reachable(get_object_directory(),
|
||||||
commit_graph_flags,
|
commit_graph_flags,
|
||||||
&split_opts);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
close_object_store(the_repository->objects);
|
close_object_store(the_repository->objects);
|
||||||
|
@ -79,11 +79,8 @@ struct rebase_options {
|
|||||||
int allow_rerere_autoupdate;
|
int allow_rerere_autoupdate;
|
||||||
int keep_empty;
|
int keep_empty;
|
||||||
int autosquash;
|
int autosquash;
|
||||||
int ignore_whitespace;
|
|
||||||
char *gpg_sign_opt;
|
char *gpg_sign_opt;
|
||||||
int autostash;
|
int autostash;
|
||||||
int committer_date_is_author_date;
|
|
||||||
int ignore_date;
|
|
||||||
char *cmd;
|
char *cmd;
|
||||||
int allow_empty_message;
|
int allow_empty_message;
|
||||||
int rebase_merges, rebase_cousins;
|
int rebase_merges, rebase_cousins;
|
||||||
@ -102,7 +99,6 @@ struct rebase_options {
|
|||||||
|
|
||||||
static struct replay_opts get_replay_opts(const struct rebase_options *opts)
|
static struct replay_opts get_replay_opts(const struct rebase_options *opts)
|
||||||
{
|
{
|
||||||
struct strbuf strategy_buf = STRBUF_INIT;
|
|
||||||
struct replay_opts replay = REPLAY_OPTS_INIT;
|
struct replay_opts replay = REPLAY_OPTS_INIT;
|
||||||
|
|
||||||
replay.action = REPLAY_INTERACTIVE_REBASE;
|
replay.action = REPLAY_INTERACTIVE_REBASE;
|
||||||
@ -116,20 +112,10 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
|
|||||||
replay.allow_empty_message = opts->allow_empty_message;
|
replay.allow_empty_message = opts->allow_empty_message;
|
||||||
replay.verbose = opts->flags & REBASE_VERBOSE;
|
replay.verbose = opts->flags & REBASE_VERBOSE;
|
||||||
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
|
replay.reschedule_failed_exec = opts->reschedule_failed_exec;
|
||||||
replay.committer_date_is_author_date =
|
|
||||||
opts->committer_date_is_author_date;
|
|
||||||
replay.ignore_date = opts->ignore_date;
|
|
||||||
replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
|
replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
|
||||||
replay.strategy = opts->strategy;
|
replay.strategy = opts->strategy;
|
||||||
|
|
||||||
if (opts->strategy_opts)
|
if (opts->strategy_opts)
|
||||||
strbuf_addstr(&strategy_buf, opts->strategy_opts);
|
parse_strategy_opts(&replay, opts->strategy_opts);
|
||||||
if (opts->ignore_whitespace)
|
|
||||||
strbuf_addstr(&strategy_buf, " --ignore-space-change");
|
|
||||||
if (strategy_buf.len)
|
|
||||||
parse_strategy_opts(&replay, strategy_buf.buf);
|
|
||||||
|
|
||||||
strbuf_release(&strategy_buf);
|
|
||||||
|
|
||||||
if (opts->squash_onto) {
|
if (opts->squash_onto) {
|
||||||
oidcpy(&replay.squash_onto, opts->squash_onto);
|
oidcpy(&replay.squash_onto, opts->squash_onto);
|
||||||
@ -531,8 +517,6 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
|
|||||||
argc = parse_options(argc, argv, prefix, options,
|
argc = parse_options(argc, argv, prefix, options,
|
||||||
builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
|
builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
|
||||||
|
|
||||||
opts.strategy_opts = xstrdup_or_null(opts.strategy_opts);
|
|
||||||
|
|
||||||
if (!is_null_oid(&squash_onto))
|
if (!is_null_oid(&squash_onto))
|
||||||
opts.squash_onto = &squash_onto;
|
opts.squash_onto = &squash_onto;
|
||||||
|
|
||||||
@ -986,12 +970,6 @@ static int run_am(struct rebase_options *opts)
|
|||||||
am.git_cmd = 1;
|
am.git_cmd = 1;
|
||||||
argv_array_push(&am.args, "am");
|
argv_array_push(&am.args, "am");
|
||||||
|
|
||||||
if (opts->ignore_whitespace)
|
|
||||||
argv_array_push(&am.args, "--ignore-whitespace");
|
|
||||||
if (opts->committer_date_is_author_date)
|
|
||||||
argv_array_push(&opts->git_am_opts, "--committer-date-is-author-date");
|
|
||||||
if (opts->ignore_date)
|
|
||||||
argv_array_push(&opts->git_am_opts, "--ignore-date");
|
|
||||||
if (opts->action && !strcmp("continue", opts->action)) {
|
if (opts->action && !strcmp("continue", opts->action)) {
|
||||||
argv_array_push(&am.args, "--resolved");
|
argv_array_push(&am.args, "--resolved");
|
||||||
argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
|
argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
|
||||||
@ -1459,17 +1437,16 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
|
PARSE_OPT_NOARG, NULL, REBASE_DIFFSTAT },
|
||||||
OPT_BOOL(0, "signoff", &options.signoff,
|
OPT_BOOL(0, "signoff", &options.signoff,
|
||||||
N_("add a Signed-off-by: line to each commit")),
|
N_("add a Signed-off-by: line to each commit")),
|
||||||
OPT_BOOL(0, "committer-date-is-author-date",
|
OPT_PASSTHRU_ARGV(0, "ignore-whitespace", &options.git_am_opts,
|
||||||
&options.committer_date_is_author_date,
|
NULL, N_("passed to 'git am'"),
|
||||||
N_("make committer date match author date")),
|
PARSE_OPT_NOARG),
|
||||||
OPT_BOOL(0, "reset-author-date", &options.ignore_date,
|
OPT_PASSTHRU_ARGV(0, "committer-date-is-author-date",
|
||||||
N_("ignore author date and use current date")),
|
&options.git_am_opts, NULL,
|
||||||
OPT_HIDDEN_BOOL(0, "ignore-date", &options.ignore_date,
|
N_("passed to 'git am'"), PARSE_OPT_NOARG),
|
||||||
N_("synonym of --reset-author-date")),
|
OPT_PASSTHRU_ARGV(0, "ignore-date", &options.git_am_opts, NULL,
|
||||||
|
N_("passed to 'git am'"), PARSE_OPT_NOARG),
|
||||||
OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
|
OPT_PASSTHRU_ARGV('C', NULL, &options.git_am_opts, N_("n"),
|
||||||
N_("passed to 'git apply'"), 0),
|
N_("passed to 'git apply'"), 0),
|
||||||
OPT_BOOL(0, "ignore-whitespace", &options.ignore_whitespace,
|
|
||||||
N_("ignore changes in whitespace")),
|
|
||||||
OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts,
|
OPT_PASSTHRU_ARGV(0, "whitespace", &options.git_am_opts,
|
||||||
N_("action"), N_("passed to 'git apply'"), 0),
|
N_("action"), N_("passed to 'git apply'"), 0),
|
||||||
OPT_BIT('f', "force-rebase", &options.flags,
|
OPT_BIT('f', "force-rebase", &options.flags,
|
||||||
@ -1742,13 +1719,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
|||||||
state_dir_base, cmd_live_rebase, buf.buf);
|
state_dir_base, cmd_live_rebase, buf.buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.committer_date_is_author_date ||
|
|
||||||
options.ignore_date)
|
|
||||||
options.flags |= REBASE_FORCE;
|
|
||||||
|
|
||||||
for (i = 0; i < options.git_am_opts.argc; i++) {
|
for (i = 0; i < options.git_am_opts.argc; i++) {
|
||||||
const char *option = options.git_am_opts.argv[i], *p;
|
const char *option = options.git_am_opts.argv[i], *p;
|
||||||
if (!strcmp(option, "--whitespace=fix") ||
|
if (!strcmp(option, "--committer-date-is-author-date") ||
|
||||||
|
!strcmp(option, "--ignore-date") ||
|
||||||
|
!strcmp(option, "--whitespace=fix") ||
|
||||||
!strcmp(option, "--whitespace=strip"))
|
!strcmp(option, "--whitespace=strip"))
|
||||||
options.flags |= REBASE_FORCE;
|
options.flags |= REBASE_FORCE;
|
||||||
else if (skip_prefix(option, "-C", &p)) {
|
else if (skip_prefix(option, "-C", &p)) {
|
||||||
|
@ -53,6 +53,8 @@ static int sparse_checkout_list(int argc, const char **argv)
|
|||||||
|
|
||||||
memset(&pl, 0, sizeof(pl));
|
memset(&pl, 0, sizeof(pl));
|
||||||
|
|
||||||
|
pl.use_cone_patterns = core_sparse_checkout_cone;
|
||||||
|
|
||||||
sparse_filename = get_sparse_checkout_filename();
|
sparse_filename = get_sparse_checkout_filename();
|
||||||
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
|
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
|
||||||
free(sparse_filename);
|
free(sparse_filename);
|
||||||
@ -62,6 +64,25 @@ static int sparse_checkout_list(int argc, const char **argv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pl.use_cone_patterns) {
|
||||||
|
int i;
|
||||||
|
struct pattern_entry *pe;
|
||||||
|
struct hashmap_iter iter;
|
||||||
|
struct string_list sl = STRING_LIST_INIT_DUP;
|
||||||
|
|
||||||
|
hashmap_for_each_entry(&pl.recursive_hashmap, &iter, pe, ent) {
|
||||||
|
/* pe->pattern starts with "/", skip it */
|
||||||
|
string_list_insert(&sl, pe->pattern + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
string_list_sort(&sl);
|
||||||
|
|
||||||
|
for (i = 0; i < sl.nr; i++)
|
||||||
|
printf("%s\n", sl.items[i].string);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
write_patterns_to_file(stdout, &pl);
|
write_patterns_to_file(stdout, &pl);
|
||||||
clear_pattern_list(&pl);
|
clear_pattern_list(&pl);
|
||||||
|
|
||||||
|
4
cache.h
4
cache.h
@ -958,8 +958,8 @@ extern int protect_hfs;
|
|||||||
extern int protect_ntfs;
|
extern int protect_ntfs;
|
||||||
extern const char *core_fsmonitor;
|
extern const char *core_fsmonitor;
|
||||||
|
|
||||||
int core_apply_sparse_checkout;
|
extern int core_apply_sparse_checkout;
|
||||||
int core_sparse_checkout_cone;
|
extern int core_sparse_checkout_cone;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Include broken refs in all ref iterations, which will
|
* Include broken refs in all ref iterations, which will
|
||||||
|
@ -1542,7 +1542,9 @@ static void split_graph_merge_strategy(struct write_commit_graph_context *ctx)
|
|||||||
|
|
||||||
if (ctx->split_opts) {
|
if (ctx->split_opts) {
|
||||||
max_commits = ctx->split_opts->max_commits;
|
max_commits = ctx->split_opts->max_commits;
|
||||||
size_mult = ctx->split_opts->size_multiple;
|
|
||||||
|
if (ctx->split_opts->size_multiple)
|
||||||
|
size_mult = ctx->split_opts->size_multiple;
|
||||||
}
|
}
|
||||||
|
|
||||||
g = ctx->r->objects->commit_graph;
|
g = ctx->r->objects->commit_graph;
|
||||||
|
@ -30,8 +30,8 @@ along with this program; if not, see <http://www.gnu.org/licenses/>.}]
|
|||||||
##
|
##
|
||||||
## Tcl/Tk sanity check
|
## Tcl/Tk sanity check
|
||||||
|
|
||||||
if {[catch {package require Tcl 8.4} err]
|
if {[catch {package require Tcl 8.6} err]
|
||||||
|| [catch {package require Tk 8.4} err]
|
|| [catch {package require Tk 8.6} err]
|
||||||
} {
|
} {
|
||||||
catch {wm withdraw .}
|
catch {wm withdraw .}
|
||||||
tk_messageBox \
|
tk_messageBox \
|
||||||
@ -684,6 +684,7 @@ proc load_current_branch {} {
|
|||||||
global current_branch is_detached
|
global current_branch is_detached
|
||||||
|
|
||||||
set fd [open [gitdir HEAD] r]
|
set fd [open [gitdir HEAD] r]
|
||||||
|
fconfigure $fd -translation binary -encoding utf-8
|
||||||
if {[gets $fd ref] < 1} {
|
if {[gets $fd ref] < 1} {
|
||||||
set ref {}
|
set ref {}
|
||||||
}
|
}
|
||||||
@ -1797,10 +1798,10 @@ proc ui_status {msg} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc ui_ready {{test {}}} {
|
proc ui_ready {} {
|
||||||
global main_status
|
global main_status
|
||||||
if {[info exists main_status]} {
|
if {[info exists main_status]} {
|
||||||
$main_status show [mc "Ready."] $test
|
$main_status show [mc "Ready."]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2150,8 +2151,6 @@ proc incr_font_size {font {amt 1}} {
|
|||||||
##
|
##
|
||||||
## ui commands
|
## ui commands
|
||||||
|
|
||||||
set starting_gitk_msg [mc "Starting gitk... please wait..."]
|
|
||||||
|
|
||||||
proc do_gitk {revs {is_submodule false}} {
|
proc do_gitk {revs {is_submodule false}} {
|
||||||
global current_diff_path file_states current_diff_side ui_index
|
global current_diff_path file_states current_diff_side ui_index
|
||||||
global _gitdir _gitworktree
|
global _gitdir _gitworktree
|
||||||
@ -2206,10 +2205,11 @@ proc do_gitk {revs {is_submodule false}} {
|
|||||||
set env(GIT_WORK_TREE) $_gitworktree
|
set env(GIT_WORK_TREE) $_gitworktree
|
||||||
cd $pwd
|
cd $pwd
|
||||||
|
|
||||||
ui_status $::starting_gitk_msg
|
set status_operation [$::main_status \
|
||||||
after 10000 {
|
start \
|
||||||
ui_ready $starting_gitk_msg
|
[mc "Starting %s... please wait..." "gitk"]]
|
||||||
}
|
|
||||||
|
after 3500 [list $status_operation stop]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2240,16 +2240,16 @@ proc do_git_gui {} {
|
|||||||
set env(GIT_WORK_TREE) $_gitworktree
|
set env(GIT_WORK_TREE) $_gitworktree
|
||||||
cd $pwd
|
cd $pwd
|
||||||
|
|
||||||
ui_status $::starting_gitk_msg
|
set status_operation [$::main_status \
|
||||||
after 10000 {
|
start \
|
||||||
ui_ready $starting_gitk_msg
|
[mc "Starting %s... please wait..." "git-gui"]]
|
||||||
}
|
|
||||||
|
after 3500 [list $status_operation stop]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc do_explore {} {
|
# Get the system-specific explorer app/command.
|
||||||
global _gitworktree
|
proc get_explorer {} {
|
||||||
set explorer {}
|
|
||||||
if {[is_Cygwin] || [is_Windows]} {
|
if {[is_Cygwin] || [is_Windows]} {
|
||||||
set explorer "explorer.exe"
|
set explorer "explorer.exe"
|
||||||
} elseif {[is_MacOSX]} {
|
} elseif {[is_MacOSX]} {
|
||||||
@ -2258,9 +2258,23 @@ proc do_explore {} {
|
|||||||
# freedesktop.org-conforming system is our best shot
|
# freedesktop.org-conforming system is our best shot
|
||||||
set explorer "xdg-open"
|
set explorer "xdg-open"
|
||||||
}
|
}
|
||||||
|
return $explorer
|
||||||
|
}
|
||||||
|
|
||||||
|
proc do_explore {} {
|
||||||
|
global _gitworktree
|
||||||
|
set explorer [get_explorer]
|
||||||
eval exec $explorer [list [file nativename $_gitworktree]] &
|
eval exec $explorer [list [file nativename $_gitworktree]] &
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Open file relative to the working tree by the default associated app.
|
||||||
|
proc do_file_open {file} {
|
||||||
|
global _gitworktree
|
||||||
|
set explorer [get_explorer]
|
||||||
|
set full_file_path [file join $_gitworktree $file]
|
||||||
|
exec $explorer [file nativename $full_file_path] &
|
||||||
|
}
|
||||||
|
|
||||||
set is_quitting 0
|
set is_quitting 0
|
||||||
set ret_code 1
|
set ret_code 1
|
||||||
|
|
||||||
@ -3512,9 +3526,11 @@ tlabel .vpane.lower.diff.header.file \
|
|||||||
-justify left
|
-justify left
|
||||||
tlabel .vpane.lower.diff.header.path \
|
tlabel .vpane.lower.diff.header.path \
|
||||||
-background gold \
|
-background gold \
|
||||||
-foreground black \
|
-foreground blue \
|
||||||
-anchor w \
|
-anchor w \
|
||||||
-justify left
|
-justify left \
|
||||||
|
-font [eval font create [font configure font_ui] -underline 1] \
|
||||||
|
-cursor hand2
|
||||||
pack .vpane.lower.diff.header.status -side left
|
pack .vpane.lower.diff.header.status -side left
|
||||||
pack .vpane.lower.diff.header.file -side left
|
pack .vpane.lower.diff.header.file -side left
|
||||||
pack .vpane.lower.diff.header.path -fill x
|
pack .vpane.lower.diff.header.path -fill x
|
||||||
@ -3529,8 +3545,12 @@ $ctxm add command \
|
|||||||
-type STRING \
|
-type STRING \
|
||||||
-- $current_diff_path
|
-- $current_diff_path
|
||||||
}
|
}
|
||||||
|
$ctxm add command \
|
||||||
|
-label [mc Open] \
|
||||||
|
-command {do_file_open $current_diff_path}
|
||||||
lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
|
lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
|
||||||
bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y"
|
bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y"
|
||||||
|
bind .vpane.lower.diff.header.path <Button-1> {do_file_open $current_diff_path}
|
||||||
|
|
||||||
# -- Diff Body
|
# -- Diff Body
|
||||||
#
|
#
|
||||||
@ -4159,6 +4179,9 @@ if {$picked && [is_config_true gui.autoexplore]} {
|
|||||||
do_explore
|
do_explore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Clear "Initializing..." status
|
||||||
|
after 500 {$main_status show ""}
|
||||||
|
|
||||||
# Local variables:
|
# Local variables:
|
||||||
# mode: tcl
|
# mode: tcl
|
||||||
# indent-tabs-mode: t
|
# indent-tabs-mode: t
|
||||||
|
@ -24,6 +24,7 @@ field w_cviewer ; # pane showing commit message
|
|||||||
field finder ; # find mini-dialog frame
|
field finder ; # find mini-dialog frame
|
||||||
field gotoline ; # line goto mini-dialog frame
|
field gotoline ; # line goto mini-dialog frame
|
||||||
field status ; # status mega-widget instance
|
field status ; # status mega-widget instance
|
||||||
|
field status_operation ; # operation displayed by status mega-widget
|
||||||
field old_height ; # last known height of $w.file_pane
|
field old_height ; # last known height of $w.file_pane
|
||||||
|
|
||||||
|
|
||||||
@ -274,6 +275,7 @@ constructor new {i_commit i_path i_jump} {
|
|||||||
pack $w_cviewer -expand 1 -fill both
|
pack $w_cviewer -expand 1 -fill both
|
||||||
|
|
||||||
set status [::status_bar::new $w.status]
|
set status [::status_bar::new $w.status]
|
||||||
|
set status_operation {}
|
||||||
|
|
||||||
menu $w.ctxm -tearoff 0
|
menu $w.ctxm -tearoff 0
|
||||||
$w.ctxm add command \
|
$w.ctxm add command \
|
||||||
@ -602,16 +604,23 @@ method _exec_blame {cur_w cur_d options cur_s} {
|
|||||||
} else {
|
} else {
|
||||||
lappend options $commit
|
lappend options $commit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# We may recurse in from another call to _exec_blame and already have
|
||||||
|
# a status operation.
|
||||||
|
if {$status_operation == {}} {
|
||||||
|
set status_operation [$status start \
|
||||||
|
$cur_s \
|
||||||
|
[mc "lines annotated"]]
|
||||||
|
} else {
|
||||||
|
$status_operation restart $cur_s
|
||||||
|
}
|
||||||
|
|
||||||
lappend options -- $path
|
lappend options -- $path
|
||||||
set fd [eval git_read --nice blame $options]
|
set fd [eval git_read --nice blame $options]
|
||||||
fconfigure $fd -blocking 0 -translation lf -encoding utf-8
|
fconfigure $fd -blocking 0 -translation lf -encoding utf-8
|
||||||
fileevent $fd readable [cb _read_blame $fd $cur_w $cur_d]
|
fileevent $fd readable [cb _read_blame $fd $cur_w $cur_d]
|
||||||
set current_fd $fd
|
set current_fd $fd
|
||||||
set blame_lines 0
|
set blame_lines 0
|
||||||
|
|
||||||
$status start \
|
|
||||||
$cur_s \
|
|
||||||
[mc "lines annotated"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
method _read_blame {fd cur_w cur_d} {
|
method _read_blame {fd cur_w cur_d} {
|
||||||
@ -806,10 +815,11 @@ method _read_blame {fd cur_w cur_d} {
|
|||||||
[mc "Loading original location annotations..."]
|
[mc "Loading original location annotations..."]
|
||||||
} else {
|
} else {
|
||||||
set current_fd {}
|
set current_fd {}
|
||||||
$status stop [mc "Annotation complete."]
|
$status_operation stop [mc "Annotation complete."]
|
||||||
|
set status_operation {}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$status update $blame_lines $total_lines
|
$status_operation update $blame_lines $total_lines
|
||||||
}
|
}
|
||||||
} ifdeleted { catch {close $fd} }
|
} ifdeleted { catch {close $fd} }
|
||||||
|
|
||||||
@ -1124,7 +1134,7 @@ method _blameparent {} {
|
|||||||
set diffcmd [list diff-tree --unified=0 $cparent $cmit -- $new_path]
|
set diffcmd [list diff-tree --unified=0 $cparent $cmit -- $new_path]
|
||||||
}
|
}
|
||||||
if {[catch {set fd [eval git_read $diffcmd]} err]} {
|
if {[catch {set fd [eval git_read $diffcmd]} err]} {
|
||||||
$status stop [mc "Unable to display parent"]
|
$status_operation stop [mc "Unable to display parent"]
|
||||||
error_popup [strcat [mc "Error loading diff:"] "\n\n$err"]
|
error_popup [strcat [mc "Error loading diff:"] "\n\n$err"]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ proc load_all_heads {} {
|
|||||||
set rh_len [expr {[string length $rh] + 1}]
|
set rh_len [expr {[string length $rh] + 1}]
|
||||||
set all_heads [list]
|
set all_heads [list]
|
||||||
set fd [git_read for-each-ref --format=%(refname) $rh]
|
set fd [git_read for-each-ref --format=%(refname) $rh]
|
||||||
|
fconfigure $fd -translation binary -encoding utf-8
|
||||||
while {[gets $fd line] > 0} {
|
while {[gets $fd line] > 0} {
|
||||||
if {!$some_heads_tracking || ![is_tracking_branch $line]} {
|
if {!$some_heads_tracking || ![is_tracking_branch $line]} {
|
||||||
lappend all_heads [string range $line $rh_len end]
|
lappend all_heads [string range $line $rh_len end]
|
||||||
@ -24,6 +25,7 @@ proc load_all_tags {} {
|
|||||||
--sort=-taggerdate \
|
--sort=-taggerdate \
|
||||||
--format=%(refname) \
|
--format=%(refname) \
|
||||||
refs/tags]
|
refs/tags]
|
||||||
|
fconfigure $fd -translation binary -encoding utf-8
|
||||||
while {[gets $fd line] > 0} {
|
while {[gets $fd line] > 0} {
|
||||||
if {![regsub ^refs/tags/ $line {} name]} continue
|
if {![regsub ^refs/tags/ $line {} name]} continue
|
||||||
lappend all_tags $name
|
lappend all_tags $name
|
||||||
|
@ -341,9 +341,9 @@ method _readtree {} {
|
|||||||
global HEAD
|
global HEAD
|
||||||
|
|
||||||
set readtree_d {}
|
set readtree_d {}
|
||||||
$::main_status start \
|
set status_bar_operation [$::main_status start \
|
||||||
[mc "Updating working directory to '%s'..." [_name $this]] \
|
[mc "Updating working directory to '%s'..." [_name $this]] \
|
||||||
[mc "files checked out"]
|
[mc "files checked out"]]
|
||||||
|
|
||||||
set fd [git_read --stderr read-tree \
|
set fd [git_read --stderr read-tree \
|
||||||
-m \
|
-m \
|
||||||
@ -354,26 +354,27 @@ method _readtree {} {
|
|||||||
$new_hash \
|
$new_hash \
|
||||||
]
|
]
|
||||||
fconfigure $fd -blocking 0 -translation binary
|
fconfigure $fd -blocking 0 -translation binary
|
||||||
fileevent $fd readable [cb _readtree_wait $fd]
|
fileevent $fd readable [cb _readtree_wait $fd $status_bar_operation]
|
||||||
}
|
}
|
||||||
|
|
||||||
method _readtree_wait {fd} {
|
method _readtree_wait {fd status_bar_operation} {
|
||||||
global current_branch
|
global current_branch
|
||||||
|
|
||||||
set buf [read $fd]
|
set buf [read $fd]
|
||||||
$::main_status update_meter $buf
|
$status_bar_operation update_meter $buf
|
||||||
append readtree_d $buf
|
append readtree_d $buf
|
||||||
|
|
||||||
fconfigure $fd -blocking 1
|
fconfigure $fd -blocking 1
|
||||||
if {![eof $fd]} {
|
if {![eof $fd]} {
|
||||||
fconfigure $fd -blocking 0
|
fconfigure $fd -blocking 0
|
||||||
|
$status_bar_operation stop
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if {[catch {close $fd}]} {
|
if {[catch {close $fd}]} {
|
||||||
set err $readtree_d
|
set err $readtree_d
|
||||||
regsub {^fatal: } $err {} err
|
regsub {^fatal: } $err {} err
|
||||||
$::main_status stop [mc "Aborted checkout of '%s' (file level merging is required)." [_name $this]]
|
$status_bar_operation stop [mc "Aborted checkout of '%s' (file level merging is required)." [_name $this]]
|
||||||
warn_popup [strcat [mc "File level merge required."] "
|
warn_popup [strcat [mc "File level merge required."] "
|
||||||
|
|
||||||
$err
|
$err
|
||||||
@ -384,7 +385,7 @@ $err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
$::main_status stop
|
$status_bar_operation stop
|
||||||
_after_readtree $this
|
_after_readtree $this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,18 @@ field w_body ; # Widget holding the center content
|
|||||||
field w_next ; # Next button
|
field w_next ; # Next button
|
||||||
field w_quit ; # Quit button
|
field w_quit ; # Quit button
|
||||||
field o_cons ; # Console object (if active)
|
field o_cons ; # Console object (if active)
|
||||||
|
|
||||||
|
# Status mega-widget instance during _do_clone2 (used by _copy_files and
|
||||||
|
# _link_files). Widget is destroyed before _do_clone2 calls
|
||||||
|
# _do_clone_checkout
|
||||||
|
field o_status
|
||||||
|
|
||||||
|
# Operation displayed by status mega-widget during _do_clone_checkout =>
|
||||||
|
# _readtree_wait => _postcheckout_wait => _do_clone_submodules =>
|
||||||
|
# _do_validate_submodule_cloning. The status mega-widget is a different
|
||||||
|
# instance than that stored in $o_status in earlier operations.
|
||||||
|
field o_status_op
|
||||||
|
|
||||||
field w_types ; # List of type buttons in clone
|
field w_types ; # List of type buttons in clone
|
||||||
field w_recentlist ; # Listbox containing recent repositories
|
field w_recentlist ; # Listbox containing recent repositories
|
||||||
field w_localpath ; # Entry widget bound to local_path
|
field w_localpath ; # Entry widget bound to local_path
|
||||||
@ -659,12 +671,12 @@ method _do_clone2 {} {
|
|||||||
|
|
||||||
switch -exact -- $clone_type {
|
switch -exact -- $clone_type {
|
||||||
hardlink {
|
hardlink {
|
||||||
set o_cons [status_bar::two_line $w_body]
|
set o_status [status_bar::two_line $w_body]
|
||||||
pack $w_body -fill x -padx 10 -pady 10
|
pack $w_body -fill x -padx 10 -pady 10
|
||||||
|
|
||||||
$o_cons start \
|
set status_op [$o_status start \
|
||||||
[mc "Counting objects"] \
|
[mc "Counting objects"] \
|
||||||
[mc "buckets"]
|
[mc "buckets"]]
|
||||||
update
|
update
|
||||||
|
|
||||||
if {[file exists [file join $objdir info alternates]]} {
|
if {[file exists [file join $objdir info alternates]]} {
|
||||||
@ -689,6 +701,7 @@ method _do_clone2 {} {
|
|||||||
} err]} {
|
} err]} {
|
||||||
catch {cd $pwd}
|
catch {cd $pwd}
|
||||||
_clone_failed $this [mc "Unable to copy objects/info/alternates: %s" $err]
|
_clone_failed $this [mc "Unable to copy objects/info/alternates: %s" $err]
|
||||||
|
$status_op stop
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -700,7 +713,7 @@ method _do_clone2 {} {
|
|||||||
-directory [file join $objdir] ??]
|
-directory [file join $objdir] ??]
|
||||||
set bcnt [expr {[llength $buckets] + 2}]
|
set bcnt [expr {[llength $buckets] + 2}]
|
||||||
set bcur 1
|
set bcur 1
|
||||||
$o_cons update $bcur $bcnt
|
$status_op update $bcur $bcnt
|
||||||
update
|
update
|
||||||
|
|
||||||
file mkdir [file join .git objects pack]
|
file mkdir [file join .git objects pack]
|
||||||
@ -708,7 +721,7 @@ method _do_clone2 {} {
|
|||||||
-directory [file join $objdir pack] *] {
|
-directory [file join $objdir pack] *] {
|
||||||
lappend tolink [file join pack $i]
|
lappend tolink [file join pack $i]
|
||||||
}
|
}
|
||||||
$o_cons update [incr bcur] $bcnt
|
$status_op update [incr bcur] $bcnt
|
||||||
update
|
update
|
||||||
|
|
||||||
foreach i $buckets {
|
foreach i $buckets {
|
||||||
@ -717,10 +730,10 @@ method _do_clone2 {} {
|
|||||||
-directory [file join $objdir $i] *] {
|
-directory [file join $objdir $i] *] {
|
||||||
lappend tolink [file join $i $j]
|
lappend tolink [file join $i $j]
|
||||||
}
|
}
|
||||||
$o_cons update [incr bcur] $bcnt
|
$status_op update [incr bcur] $bcnt
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
$o_cons stop
|
$status_op stop
|
||||||
|
|
||||||
if {$tolink eq {}} {
|
if {$tolink eq {}} {
|
||||||
info_popup [strcat \
|
info_popup [strcat \
|
||||||
@ -747,6 +760,8 @@ method _do_clone2 {} {
|
|||||||
if {!$i} return
|
if {!$i} return
|
||||||
|
|
||||||
destroy $w_body
|
destroy $w_body
|
||||||
|
|
||||||
|
set o_status {}
|
||||||
}
|
}
|
||||||
full {
|
full {
|
||||||
set o_cons [console::embed \
|
set o_cons [console::embed \
|
||||||
@ -781,9 +796,9 @@ method _do_clone2 {} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
method _copy_files {objdir tocopy} {
|
method _copy_files {objdir tocopy} {
|
||||||
$o_cons start \
|
set status_op [$o_status start \
|
||||||
[mc "Copying objects"] \
|
[mc "Copying objects"] \
|
||||||
[mc "KiB"]
|
[mc "KiB"]]
|
||||||
set tot 0
|
set tot 0
|
||||||
set cmp 0
|
set cmp 0
|
||||||
foreach p $tocopy {
|
foreach p $tocopy {
|
||||||
@ -798,7 +813,7 @@ method _copy_files {objdir tocopy} {
|
|||||||
|
|
||||||
while {![eof $f_in]} {
|
while {![eof $f_in]} {
|
||||||
incr cmp [fcopy $f_in $f_cp -size 16384]
|
incr cmp [fcopy $f_in $f_cp -size 16384]
|
||||||
$o_cons update \
|
$status_op update \
|
||||||
[expr {$cmp / 1024}] \
|
[expr {$cmp / 1024}] \
|
||||||
[expr {$tot / 1024}]
|
[expr {$tot / 1024}]
|
||||||
update
|
update
|
||||||
@ -808,17 +823,19 @@ method _copy_files {objdir tocopy} {
|
|||||||
close $f_cp
|
close $f_cp
|
||||||
} err]} {
|
} err]} {
|
||||||
_clone_failed $this [mc "Unable to copy object: %s" $err]
|
_clone_failed $this [mc "Unable to copy object: %s" $err]
|
||||||
|
$status_op stop
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$status_op stop
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
method _link_files {objdir tolink} {
|
method _link_files {objdir tolink} {
|
||||||
set total [llength $tolink]
|
set total [llength $tolink]
|
||||||
$o_cons start \
|
set status_op [$o_status start \
|
||||||
[mc "Linking objects"] \
|
[mc "Linking objects"] \
|
||||||
[mc "objects"]
|
[mc "objects"]]
|
||||||
for {set i 0} {$i < $total} {} {
|
for {set i 0} {$i < $total} {} {
|
||||||
set p [lindex $tolink $i]
|
set p [lindex $tolink $i]
|
||||||
if {[catch {
|
if {[catch {
|
||||||
@ -827,15 +844,17 @@ method _link_files {objdir tolink} {
|
|||||||
[file join $objdir $p]
|
[file join $objdir $p]
|
||||||
} err]} {
|
} err]} {
|
||||||
_clone_failed $this [mc "Unable to hardlink object: %s" $err]
|
_clone_failed $this [mc "Unable to hardlink object: %s" $err]
|
||||||
|
$status_op stop
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
incr i
|
incr i
|
||||||
if {$i % 5 == 0} {
|
if {$i % 5 == 0} {
|
||||||
$o_cons update $i $total
|
$status_op update $i $total
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$status_op stop
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -958,11 +977,26 @@ method _do_clone_checkout {HEAD} {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
set o_cons [status_bar::two_line $w_body]
|
set status [status_bar::two_line $w_body]
|
||||||
pack $w_body -fill x -padx 10 -pady 10
|
pack $w_body -fill x -padx 10 -pady 10
|
||||||
$o_cons start \
|
|
||||||
|
# We start the status operation here.
|
||||||
|
#
|
||||||
|
# This function calls _readtree_wait as a callback.
|
||||||
|
#
|
||||||
|
# _readtree_wait in turn either calls _do_clone_submodules directly,
|
||||||
|
# or calls _postcheckout_wait as a callback which then calls
|
||||||
|
# _do_clone_submodules.
|
||||||
|
#
|
||||||
|
# _do_clone_submodules calls _do_validate_submodule_cloning.
|
||||||
|
#
|
||||||
|
# _do_validate_submodule_cloning stops the status operation.
|
||||||
|
#
|
||||||
|
# There are no other calls into this chain from other code.
|
||||||
|
|
||||||
|
set o_status_op [$status start \
|
||||||
[mc "Creating working directory"] \
|
[mc "Creating working directory"] \
|
||||||
[mc "files"]
|
[mc "files"]]
|
||||||
|
|
||||||
set readtree_err {}
|
set readtree_err {}
|
||||||
set fd [git_read --stderr read-tree \
|
set fd [git_read --stderr read-tree \
|
||||||
@ -976,33 +1010,9 @@ method _do_clone_checkout {HEAD} {
|
|||||||
fileevent $fd readable [cb _readtree_wait $fd]
|
fileevent $fd readable [cb _readtree_wait $fd]
|
||||||
}
|
}
|
||||||
|
|
||||||
method _do_validate_submodule_cloning {ok} {
|
|
||||||
if {$ok} {
|
|
||||||
$o_cons done $ok
|
|
||||||
set done 1
|
|
||||||
} else {
|
|
||||||
_clone_failed $this [mc "Cannot clone submodules."]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
method _do_clone_submodules {} {
|
|
||||||
if {$recursive eq {true}} {
|
|
||||||
destroy $w_body
|
|
||||||
set o_cons [console::embed \
|
|
||||||
$w_body \
|
|
||||||
[mc "Cloning submodules"]]
|
|
||||||
pack $w_body -fill both -expand 1 -padx 10
|
|
||||||
$o_cons exec \
|
|
||||||
[list git submodule update --init --recursive] \
|
|
||||||
[cb _do_validate_submodule_cloning]
|
|
||||||
} else {
|
|
||||||
set done 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
method _readtree_wait {fd} {
|
method _readtree_wait {fd} {
|
||||||
set buf [read $fd]
|
set buf [read $fd]
|
||||||
$o_cons update_meter $buf
|
$o_status_op update_meter $buf
|
||||||
append readtree_err $buf
|
append readtree_err $buf
|
||||||
|
|
||||||
fconfigure $fd -blocking 1
|
fconfigure $fd -blocking 1
|
||||||
@ -1050,6 +1060,34 @@ method _postcheckout_wait {fd_ph} {
|
|||||||
fconfigure $fd_ph -blocking 0
|
fconfigure $fd_ph -blocking 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
method _do_clone_submodules {} {
|
||||||
|
if {$recursive eq {true}} {
|
||||||
|
$o_status_op stop
|
||||||
|
set o_status_op {}
|
||||||
|
|
||||||
|
destroy $w_body
|
||||||
|
|
||||||
|
set o_cons [console::embed \
|
||||||
|
$w_body \
|
||||||
|
[mc "Cloning submodules"]]
|
||||||
|
pack $w_body -fill both -expand 1 -padx 10
|
||||||
|
$o_cons exec \
|
||||||
|
[list git submodule update --init --recursive] \
|
||||||
|
[cb _do_validate_submodule_cloning]
|
||||||
|
} else {
|
||||||
|
set done 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
method _do_validate_submodule_cloning {ok} {
|
||||||
|
if {$ok} {
|
||||||
|
$o_cons done $ok
|
||||||
|
set done 1
|
||||||
|
} else {
|
||||||
|
_clone_failed $this [mc "Cannot clone submodules."]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
##
|
##
|
||||||
## Open Existing Repository
|
## Open Existing Repository
|
||||||
|
160
git-gui/lib/chord.tcl
Normal file
160
git-gui/lib/chord.tcl
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
# Simple Chord for Tcl
|
||||||
|
#
|
||||||
|
# A "chord" is a method with more than one entrypoint and only one body, such
|
||||||
|
# that the body runs only once all the entrypoints have been called by
|
||||||
|
# different asynchronous tasks. In this implementation, the chord is defined
|
||||||
|
# dynamically for each invocation. A SimpleChord object is created, supplying
|
||||||
|
# body script to be run when the chord is completed, and then one or more notes
|
||||||
|
# are added to the chord. Each note can be called like a proc, and returns
|
||||||
|
# immediately if the chord isn't yet complete. When the last remaining note is
|
||||||
|
# called, the body runs before the note returns.
|
||||||
|
#
|
||||||
|
# The SimpleChord class has a constructor that takes the body script, and a
|
||||||
|
# method add_note that returns a note object. Since the body script does not
|
||||||
|
# run in the context of the procedure that defined it, a mechanism is provided
|
||||||
|
# for injecting variables into the chord for use by the body script. The
|
||||||
|
# activation of a note is idempotent; multiple calls have the same effect as
|
||||||
|
# a simple call.
|
||||||
|
#
|
||||||
|
# If you are invoking asynchronous operations with chord notes as completion
|
||||||
|
# callbacks, and there is a possibility that earlier operations could complete
|
||||||
|
# before later ones are started, it is a good practice to create a "common"
|
||||||
|
# note on the chord that prevents it from being complete until you're certain
|
||||||
|
# you've added all the notes you need.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# # Turn off the UI while running a couple of async operations.
|
||||||
|
# lock_ui
|
||||||
|
#
|
||||||
|
# set chord [SimpleChord new {
|
||||||
|
# unlock_ui
|
||||||
|
# # Note: $notice here is not referenced in the calling scope
|
||||||
|
# if {$notice} { info_popup $notice }
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# # Configure a note to keep the chord from completing until
|
||||||
|
# # all operations have been initiated.
|
||||||
|
# set common_note [$chord add_note]
|
||||||
|
#
|
||||||
|
# # Pass notes as 'after' callbacks to other operations
|
||||||
|
# async_operation $args [$chord add_note]
|
||||||
|
# other_async_operation $args [$chord add_note]
|
||||||
|
#
|
||||||
|
# # Communicate with the chord body
|
||||||
|
# if {$condition} {
|
||||||
|
# # This sets $notice in the same context that the chord body runs in.
|
||||||
|
# $chord eval { set notice "Something interesting" }
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# # Activate the common note, making the chord eligible to complete
|
||||||
|
# $common_note
|
||||||
|
#
|
||||||
|
# At this point, the chord will complete at some unknown point in the future.
|
||||||
|
# The common note might have been the first note activated, or the async
|
||||||
|
# operations might have completed synchronously and the common note is the
|
||||||
|
# last one, completing the chord before this code finishes, or anything in
|
||||||
|
# between. The purpose of the chord is to not have to worry about the order.
|
||||||
|
|
||||||
|
# SimpleChord class:
|
||||||
|
# Represents a procedure that conceptually has multiple entrypoints that must
|
||||||
|
# all be called before the procedure executes. Each entrypoint is called a
|
||||||
|
# "note". The chord is only "completed" when all the notes are "activated".
|
||||||
|
oo::class create SimpleChord {
|
||||||
|
variable notes body is_completed
|
||||||
|
|
||||||
|
# Constructor:
|
||||||
|
# set chord [SimpleChord new {body}]
|
||||||
|
# Creates a new chord object with the specified body script. The
|
||||||
|
# body script is evaluated at most once, when a note is activated
|
||||||
|
# and the chord has no other non-activated notes.
|
||||||
|
constructor {body} {
|
||||||
|
set notes [list]
|
||||||
|
my eval [list set body $body]
|
||||||
|
set is_completed 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method:
|
||||||
|
# $chord eval {script}
|
||||||
|
# Runs the specified script in the same context (namespace) in which
|
||||||
|
# the chord body will be evaluated. This can be used to set variable
|
||||||
|
# values for the chord body to use.
|
||||||
|
method eval {script} {
|
||||||
|
namespace eval [self] $script
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method:
|
||||||
|
# set note [$chord add_note]
|
||||||
|
# Adds a new note to the chord, an instance of ChordNote. Raises an
|
||||||
|
# error if the chord is already completed, otherwise the chord is
|
||||||
|
# updated so that the new note must also be activated before the
|
||||||
|
# body is evaluated.
|
||||||
|
method add_note {} {
|
||||||
|
if {$is_completed} { error "Cannot add a note to a completed chord" }
|
||||||
|
|
||||||
|
set note [ChordNote new [self]]
|
||||||
|
|
||||||
|
lappend notes $note
|
||||||
|
|
||||||
|
return $note
|
||||||
|
}
|
||||||
|
|
||||||
|
# This method is for internal use only and is intentionally undocumented.
|
||||||
|
method notify_note_activation {} {
|
||||||
|
if {!$is_completed} {
|
||||||
|
foreach note $notes {
|
||||||
|
if {![$note is_activated]} { return }
|
||||||
|
}
|
||||||
|
|
||||||
|
set is_completed 1
|
||||||
|
|
||||||
|
namespace eval [self] $body
|
||||||
|
namespace delete [self]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ChordNote class:
|
||||||
|
# Represents a note within a chord, providing a way to activate it. When the
|
||||||
|
# final note of the chord is activated (this can be any note in the chord,
|
||||||
|
# with all other notes already previously activated in any order), the chord's
|
||||||
|
# body is evaluated.
|
||||||
|
oo::class create ChordNote {
|
||||||
|
variable chord is_activated
|
||||||
|
|
||||||
|
# Constructor:
|
||||||
|
# Instances of ChordNote are created internally by calling add_note on
|
||||||
|
# SimpleChord objects.
|
||||||
|
constructor {chord} {
|
||||||
|
my eval set chord $chord
|
||||||
|
set is_activated 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method:
|
||||||
|
# [$note is_activated]
|
||||||
|
# Returns true if this note has already been activated.
|
||||||
|
method is_activated {} {
|
||||||
|
return $is_activated
|
||||||
|
}
|
||||||
|
|
||||||
|
# Method:
|
||||||
|
# $note
|
||||||
|
# Activates the note, if it has not already been activated, and
|
||||||
|
# completes the chord if there are no other notes awaiting
|
||||||
|
# activation. Subsequent calls will have no further effect.
|
||||||
|
#
|
||||||
|
# NB: In TclOO, if an object is invoked like a method without supplying
|
||||||
|
# any method name, then this internal method `unknown` is what
|
||||||
|
# actually runs (with no parameters). It is used in the ChordNote
|
||||||
|
# class for the purpose of allowing the note object to be called as
|
||||||
|
# a function (see example above). (The `unknown` method can also be
|
||||||
|
# used to support dynamic dispatch, but must take parameters to
|
||||||
|
# identify the "unknown" method to be invoked. In this form, this
|
||||||
|
# proc serves only to make instances behave directly like methods.)
|
||||||
|
method unknown {} {
|
||||||
|
if {!$is_activated} {
|
||||||
|
set is_activated 1
|
||||||
|
$chord notify_note_activation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -203,6 +203,8 @@ method done {ok} {
|
|||||||
focus $w.ok
|
focus $w.ok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bind $w <Key-Escape> "destroy $w;break"
|
||||||
}
|
}
|
||||||
|
|
||||||
method _sb_set {sb orient first last} {
|
method _sb_set {sb orient first last} {
|
||||||
|
@ -7,67 +7,74 @@ proc _delete_indexlock {} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc _close_updateindex {fd after} {
|
proc close_and_unlock_index {fd after} {
|
||||||
global use_ttk NS
|
if {![catch {_close_updateindex $fd} err]} {
|
||||||
fconfigure $fd -blocking 1
|
|
||||||
if {[catch {close $fd} err]} {
|
|
||||||
set w .indexfried
|
|
||||||
Dialog $w
|
|
||||||
wm withdraw $w
|
|
||||||
wm title $w [strcat "[appname] ([reponame]): " [mc "Index Error"]]
|
|
||||||
wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
|
|
||||||
set s [mc "Updating the Git index failed. A rescan will be automatically started to resynchronize git-gui."]
|
|
||||||
text $w.msg -yscrollcommand [list $w.vs set] \
|
|
||||||
-width [string length $s] -relief flat \
|
|
||||||
-borderwidth 0 -highlightthickness 0 \
|
|
||||||
-background [get_bg_color $w]
|
|
||||||
$w.msg tag configure bold -font font_uibold -justify center
|
|
||||||
${NS}::scrollbar $w.vs -command [list $w.msg yview]
|
|
||||||
$w.msg insert end $s bold \n\n$err {}
|
|
||||||
$w.msg configure -state disabled
|
|
||||||
|
|
||||||
${NS}::button $w.continue \
|
|
||||||
-text [mc "Continue"] \
|
|
||||||
-command [list destroy $w]
|
|
||||||
${NS}::button $w.unlock \
|
|
||||||
-text [mc "Unlock Index"] \
|
|
||||||
-command "destroy $w; _delete_indexlock"
|
|
||||||
grid $w.msg - $w.vs -sticky news
|
|
||||||
grid $w.unlock $w.continue - -sticky se -padx 2 -pady 2
|
|
||||||
grid columnconfigure $w 0 -weight 1
|
|
||||||
grid rowconfigure $w 0 -weight 1
|
|
||||||
|
|
||||||
wm protocol $w WM_DELETE_WINDOW update
|
|
||||||
bind $w.continue <Visibility> "
|
|
||||||
grab $w
|
|
||||||
focus %W
|
|
||||||
"
|
|
||||||
wm deiconify $w
|
|
||||||
tkwait window $w
|
|
||||||
|
|
||||||
$::main_status stop
|
|
||||||
unlock_index
|
unlock_index
|
||||||
rescan $after 0
|
uplevel #0 $after
|
||||||
return
|
} else {
|
||||||
|
rescan_on_error $err $after
|
||||||
}
|
}
|
||||||
|
|
||||||
$::main_status stop
|
|
||||||
unlock_index
|
|
||||||
uplevel #0 $after
|
|
||||||
}
|
}
|
||||||
|
|
||||||
proc update_indexinfo {msg pathList after} {
|
proc _close_updateindex {fd} {
|
||||||
|
fconfigure $fd -blocking 1
|
||||||
|
close $fd
|
||||||
|
}
|
||||||
|
|
||||||
|
proc rescan_on_error {err {after {}}} {
|
||||||
|
global use_ttk NS
|
||||||
|
|
||||||
|
set w .indexfried
|
||||||
|
Dialog $w
|
||||||
|
wm withdraw $w
|
||||||
|
wm title $w [strcat "[appname] ([reponame]): " [mc "Index Error"]]
|
||||||
|
wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
|
||||||
|
set s [mc "Updating the Git index failed. A rescan will be automatically started to resynchronize git-gui."]
|
||||||
|
text $w.msg -yscrollcommand [list $w.vs set] \
|
||||||
|
-width [string length $s] -relief flat \
|
||||||
|
-borderwidth 0 -highlightthickness 0 \
|
||||||
|
-background [get_bg_color $w]
|
||||||
|
$w.msg tag configure bold -font font_uibold -justify center
|
||||||
|
${NS}::scrollbar $w.vs -command [list $w.msg yview]
|
||||||
|
$w.msg insert end $s bold \n\n$err {}
|
||||||
|
$w.msg configure -state disabled
|
||||||
|
|
||||||
|
${NS}::button $w.continue \
|
||||||
|
-text [mc "Continue"] \
|
||||||
|
-command [list destroy $w]
|
||||||
|
${NS}::button $w.unlock \
|
||||||
|
-text [mc "Unlock Index"] \
|
||||||
|
-command "destroy $w; _delete_indexlock"
|
||||||
|
grid $w.msg - $w.vs -sticky news
|
||||||
|
grid $w.unlock $w.continue - -sticky se -padx 2 -pady 2
|
||||||
|
grid columnconfigure $w 0 -weight 1
|
||||||
|
grid rowconfigure $w 0 -weight 1
|
||||||
|
|
||||||
|
wm protocol $w WM_DELETE_WINDOW update
|
||||||
|
bind $w.continue <Visibility> "
|
||||||
|
grab $w
|
||||||
|
focus %W
|
||||||
|
"
|
||||||
|
wm deiconify $w
|
||||||
|
tkwait window $w
|
||||||
|
|
||||||
|
$::main_status stop_all
|
||||||
|
unlock_index
|
||||||
|
rescan [concat $after [list ui_ready]] 0
|
||||||
|
}
|
||||||
|
|
||||||
|
proc update_indexinfo {msg path_list after} {
|
||||||
global update_index_cp
|
global update_index_cp
|
||||||
|
|
||||||
if {![lock_index update]} return
|
if {![lock_index update]} return
|
||||||
|
|
||||||
set update_index_cp 0
|
set update_index_cp 0
|
||||||
set pathList [lsort $pathList]
|
set path_list [lsort $path_list]
|
||||||
set totalCnt [llength $pathList]
|
set total_cnt [llength $path_list]
|
||||||
set batch [expr {int($totalCnt * .01) + 1}]
|
set batch [expr {int($total_cnt * .01) + 1}]
|
||||||
if {$batch > 25} {set batch 25}
|
if {$batch > 25} {set batch 25}
|
||||||
|
|
||||||
$::main_status start $msg [mc "files"]
|
set status_bar_operation [$::main_status start $msg [mc "files"]]
|
||||||
set fd [git_write update-index -z --index-info]
|
set fd [git_write update-index -z --index-info]
|
||||||
fconfigure $fd \
|
fconfigure $fd \
|
||||||
-blocking 0 \
|
-blocking 0 \
|
||||||
@ -78,26 +85,29 @@ proc update_indexinfo {msg pathList after} {
|
|||||||
fileevent $fd writable [list \
|
fileevent $fd writable [list \
|
||||||
write_update_indexinfo \
|
write_update_indexinfo \
|
||||||
$fd \
|
$fd \
|
||||||
$pathList \
|
$path_list \
|
||||||
$totalCnt \
|
$total_cnt \
|
||||||
$batch \
|
$batch \
|
||||||
|
$status_bar_operation \
|
||||||
$after \
|
$after \
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
proc write_update_indexinfo {fd pathList totalCnt batch after} {
|
proc write_update_indexinfo {fd path_list total_cnt batch status_bar_operation \
|
||||||
|
after} {
|
||||||
global update_index_cp
|
global update_index_cp
|
||||||
global file_states current_diff_path
|
global file_states current_diff_path
|
||||||
|
|
||||||
if {$update_index_cp >= $totalCnt} {
|
if {$update_index_cp >= $total_cnt} {
|
||||||
_close_updateindex $fd $after
|
$status_bar_operation stop
|
||||||
|
close_and_unlock_index $fd $after
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for {set i $batch} \
|
for {set i $batch} \
|
||||||
{$update_index_cp < $totalCnt && $i > 0} \
|
{$update_index_cp < $total_cnt && $i > 0} \
|
||||||
{incr i -1} {
|
{incr i -1} {
|
||||||
set path [lindex $pathList $update_index_cp]
|
set path [lindex $path_list $update_index_cp]
|
||||||
incr update_index_cp
|
incr update_index_cp
|
||||||
|
|
||||||
set s $file_states($path)
|
set s $file_states($path)
|
||||||
@ -119,21 +129,21 @@ proc write_update_indexinfo {fd pathList totalCnt batch after} {
|
|||||||
display_file $path $new
|
display_file $path $new
|
||||||
}
|
}
|
||||||
|
|
||||||
$::main_status update $update_index_cp $totalCnt
|
$status_bar_operation update $update_index_cp $total_cnt
|
||||||
}
|
}
|
||||||
|
|
||||||
proc update_index {msg pathList after} {
|
proc update_index {msg path_list after} {
|
||||||
global update_index_cp
|
global update_index_cp
|
||||||
|
|
||||||
if {![lock_index update]} return
|
if {![lock_index update]} return
|
||||||
|
|
||||||
set update_index_cp 0
|
set update_index_cp 0
|
||||||
set pathList [lsort $pathList]
|
set path_list [lsort $path_list]
|
||||||
set totalCnt [llength $pathList]
|
set total_cnt [llength $path_list]
|
||||||
set batch [expr {int($totalCnt * .01) + 1}]
|
set batch [expr {int($total_cnt * .01) + 1}]
|
||||||
if {$batch > 25} {set batch 25}
|
if {$batch > 25} {set batch 25}
|
||||||
|
|
||||||
$::main_status start $msg [mc "files"]
|
set status_bar_operation [$::main_status start $msg [mc "files"]]
|
||||||
set fd [git_write update-index --add --remove -z --stdin]
|
set fd [git_write update-index --add --remove -z --stdin]
|
||||||
fconfigure $fd \
|
fconfigure $fd \
|
||||||
-blocking 0 \
|
-blocking 0 \
|
||||||
@ -144,26 +154,29 @@ proc update_index {msg pathList after} {
|
|||||||
fileevent $fd writable [list \
|
fileevent $fd writable [list \
|
||||||
write_update_index \
|
write_update_index \
|
||||||
$fd \
|
$fd \
|
||||||
$pathList \
|
$path_list \
|
||||||
$totalCnt \
|
$total_cnt \
|
||||||
$batch \
|
$batch \
|
||||||
|
$status_bar_operation \
|
||||||
$after \
|
$after \
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
proc write_update_index {fd pathList totalCnt batch after} {
|
proc write_update_index {fd path_list total_cnt batch status_bar_operation \
|
||||||
|
after} {
|
||||||
global update_index_cp
|
global update_index_cp
|
||||||
global file_states current_diff_path
|
global file_states current_diff_path
|
||||||
|
|
||||||
if {$update_index_cp >= $totalCnt} {
|
if {$update_index_cp >= $total_cnt} {
|
||||||
_close_updateindex $fd $after
|
$status_bar_operation stop
|
||||||
|
close_and_unlock_index $fd $after
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for {set i $batch} \
|
for {set i $batch} \
|
||||||
{$update_index_cp < $totalCnt && $i > 0} \
|
{$update_index_cp < $total_cnt && $i > 0} \
|
||||||
{incr i -1} {
|
{incr i -1} {
|
||||||
set path [lindex $pathList $update_index_cp]
|
set path [lindex $path_list $update_index_cp]
|
||||||
incr update_index_cp
|
incr update_index_cp
|
||||||
|
|
||||||
switch -glob -- [lindex $file_states($path) 0] {
|
switch -glob -- [lindex $file_states($path) 0] {
|
||||||
@ -190,21 +203,21 @@ proc write_update_index {fd pathList totalCnt batch after} {
|
|||||||
display_file $path $new
|
display_file $path $new
|
||||||
}
|
}
|
||||||
|
|
||||||
$::main_status update $update_index_cp $totalCnt
|
$status_bar_operation update $update_index_cp $total_cnt
|
||||||
}
|
}
|
||||||
|
|
||||||
proc checkout_index {msg pathList after} {
|
proc checkout_index {msg path_list after capture_error} {
|
||||||
global update_index_cp
|
global update_index_cp
|
||||||
|
|
||||||
if {![lock_index update]} return
|
if {![lock_index update]} return
|
||||||
|
|
||||||
set update_index_cp 0
|
set update_index_cp 0
|
||||||
set pathList [lsort $pathList]
|
set path_list [lsort $path_list]
|
||||||
set totalCnt [llength $pathList]
|
set total_cnt [llength $path_list]
|
||||||
set batch [expr {int($totalCnt * .01) + 1}]
|
set batch [expr {int($total_cnt * .01) + 1}]
|
||||||
if {$batch > 25} {set batch 25}
|
if {$batch > 25} {set batch 25}
|
||||||
|
|
||||||
$::main_status start $msg [mc "files"]
|
set status_bar_operation [$::main_status start $msg [mc "files"]]
|
||||||
set fd [git_write checkout-index \
|
set fd [git_write checkout-index \
|
||||||
--index \
|
--index \
|
||||||
--quiet \
|
--quiet \
|
||||||
@ -221,26 +234,45 @@ proc checkout_index {msg pathList after} {
|
|||||||
fileevent $fd writable [list \
|
fileevent $fd writable [list \
|
||||||
write_checkout_index \
|
write_checkout_index \
|
||||||
$fd \
|
$fd \
|
||||||
$pathList \
|
$path_list \
|
||||||
$totalCnt \
|
$total_cnt \
|
||||||
$batch \
|
$batch \
|
||||||
|
$status_bar_operation \
|
||||||
$after \
|
$after \
|
||||||
|
$capture_error \
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
proc write_checkout_index {fd pathList totalCnt batch after} {
|
proc write_checkout_index {fd path_list total_cnt batch status_bar_operation \
|
||||||
|
after capture_error} {
|
||||||
global update_index_cp
|
global update_index_cp
|
||||||
global file_states current_diff_path
|
global file_states current_diff_path
|
||||||
|
|
||||||
if {$update_index_cp >= $totalCnt} {
|
if {$update_index_cp >= $total_cnt} {
|
||||||
_close_updateindex $fd $after
|
$status_bar_operation stop
|
||||||
|
|
||||||
|
# We do not unlock the index directly here because this
|
||||||
|
# operation expects to potentially run in parallel with file
|
||||||
|
# deletions scheduled by revert_helper. We're done with the
|
||||||
|
# update index, so we close it, but actually unlocking the index
|
||||||
|
# and dealing with potential errors is deferred to the chord
|
||||||
|
# body that runs when all async operations are completed.
|
||||||
|
#
|
||||||
|
# (See after_chord in revert_helper.)
|
||||||
|
|
||||||
|
if {[catch {_close_updateindex $fd} err]} {
|
||||||
|
uplevel #0 $capture_error [list $err]
|
||||||
|
}
|
||||||
|
|
||||||
|
uplevel #0 $after
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for {set i $batch} \
|
for {set i $batch} \
|
||||||
{$update_index_cp < $totalCnt && $i > 0} \
|
{$update_index_cp < $total_cnt && $i > 0} \
|
||||||
{incr i -1} {
|
{incr i -1} {
|
||||||
set path [lindex $pathList $update_index_cp]
|
set path [lindex $path_list $update_index_cp]
|
||||||
incr update_index_cp
|
incr update_index_cp
|
||||||
switch -glob -- [lindex $file_states($path) 0] {
|
switch -glob -- [lindex $file_states($path) 0] {
|
||||||
U? {continue}
|
U? {continue}
|
||||||
@ -253,7 +285,7 @@ proc write_checkout_index {fd pathList totalCnt batch after} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$::main_status update $update_index_cp $totalCnt
|
$status_bar_operation update $update_index_cp $total_cnt
|
||||||
}
|
}
|
||||||
|
|
||||||
proc unstage_helper {txt paths} {
|
proc unstage_helper {txt paths} {
|
||||||
@ -261,7 +293,7 @@ proc unstage_helper {txt paths} {
|
|||||||
|
|
||||||
if {![lock_index begin-update]} return
|
if {![lock_index begin-update]} return
|
||||||
|
|
||||||
set pathList [list]
|
set path_list [list]
|
||||||
set after {}
|
set after {}
|
||||||
foreach path $paths {
|
foreach path $paths {
|
||||||
switch -glob -- [lindex $file_states($path) 0] {
|
switch -glob -- [lindex $file_states($path) 0] {
|
||||||
@ -269,19 +301,19 @@ proc unstage_helper {txt paths} {
|
|||||||
M? -
|
M? -
|
||||||
T? -
|
T? -
|
||||||
D? {
|
D? {
|
||||||
lappend pathList $path
|
lappend path_list $path
|
||||||
if {$path eq $current_diff_path} {
|
if {$path eq $current_diff_path} {
|
||||||
set after {reshow_diff;}
|
set after {reshow_diff;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if {$pathList eq {}} {
|
if {$path_list eq {}} {
|
||||||
unlock_index
|
unlock_index
|
||||||
} else {
|
} else {
|
||||||
update_indexinfo \
|
update_indexinfo \
|
||||||
$txt \
|
$txt \
|
||||||
$pathList \
|
$path_list \
|
||||||
[concat $after [list ui_ready]]
|
[concat $after [list ui_ready]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -305,7 +337,7 @@ proc add_helper {txt paths} {
|
|||||||
|
|
||||||
if {![lock_index begin-update]} return
|
if {![lock_index begin-update]} return
|
||||||
|
|
||||||
set pathList [list]
|
set path_list [list]
|
||||||
set after {}
|
set after {}
|
||||||
foreach path $paths {
|
foreach path $paths {
|
||||||
switch -glob -- [lindex $file_states($path) 0] {
|
switch -glob -- [lindex $file_states($path) 0] {
|
||||||
@ -321,19 +353,19 @@ proc add_helper {txt paths} {
|
|||||||
?M -
|
?M -
|
||||||
?D -
|
?D -
|
||||||
?T {
|
?T {
|
||||||
lappend pathList $path
|
lappend path_list $path
|
||||||
if {$path eq $current_diff_path} {
|
if {$path eq $current_diff_path} {
|
||||||
set after {reshow_diff;}
|
set after {reshow_diff;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if {$pathList eq {}} {
|
if {$path_list eq {}} {
|
||||||
unlock_index
|
unlock_index
|
||||||
} else {
|
} else {
|
||||||
update_index \
|
update_index \
|
||||||
$txt \
|
$txt \
|
||||||
$pathList \
|
$path_list \
|
||||||
[concat $after {ui_status [mc "Ready to commit."]}]
|
[concat $after {ui_status [mc "Ready to commit."]}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,66 +420,301 @@ proc do_add_all {} {
|
|||||||
add_helper [mc "Adding all changed files"] $paths
|
add_helper [mc "Adding all changed files"] $paths
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Copied from TclLib package "lambda".
|
||||||
|
proc lambda {arguments body args} {
|
||||||
|
return [list ::apply [list $arguments $body] {*}$args]
|
||||||
|
}
|
||||||
|
|
||||||
proc revert_helper {txt paths} {
|
proc revert_helper {txt paths} {
|
||||||
global file_states current_diff_path
|
global file_states current_diff_path
|
||||||
|
|
||||||
if {![lock_index begin-update]} return
|
if {![lock_index begin-update]} return
|
||||||
|
|
||||||
set pathList [list]
|
# Common "after" functionality that waits until multiple asynchronous
|
||||||
set after {}
|
# operations are complete (by waiting for them to activate their notes
|
||||||
|
# on the chord).
|
||||||
|
#
|
||||||
|
# The asynchronous operations are each indicated below by a comment
|
||||||
|
# before the code block that starts the async operation.
|
||||||
|
set after_chord [SimpleChord new {
|
||||||
|
if {[string trim $err] != ""} {
|
||||||
|
rescan_on_error $err
|
||||||
|
} else {
|
||||||
|
unlock_index
|
||||||
|
if {$should_reshow_diff} { reshow_diff }
|
||||||
|
ui_ready
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
|
||||||
|
$after_chord eval { set should_reshow_diff 0 }
|
||||||
|
|
||||||
|
# This function captures an error for processing when after_chord is
|
||||||
|
# completed. (The chord is curried into the lambda function.)
|
||||||
|
set capture_error [lambda \
|
||||||
|
{chord error} \
|
||||||
|
{ $chord eval [list set err $error] } \
|
||||||
|
$after_chord]
|
||||||
|
|
||||||
|
# We don't know how many notes we're going to create (it's dynamic based
|
||||||
|
# on conditional paths below), so create a common note that will delay
|
||||||
|
# the chord's completion until we activate it, and then activate it
|
||||||
|
# after all the other notes have been created.
|
||||||
|
set after_common_note [$after_chord add_note]
|
||||||
|
|
||||||
|
set path_list [list]
|
||||||
|
set untracked_list [list]
|
||||||
|
|
||||||
foreach path $paths {
|
foreach path $paths {
|
||||||
switch -glob -- [lindex $file_states($path) 0] {
|
switch -glob -- [lindex $file_states($path) 0] {
|
||||||
U? {continue}
|
U? {continue}
|
||||||
|
?O {
|
||||||
|
lappend untracked_list $path
|
||||||
|
}
|
||||||
?M -
|
?M -
|
||||||
?T -
|
?T -
|
||||||
?D {
|
?D {
|
||||||
lappend pathList $path
|
lappend path_list $path
|
||||||
if {$path eq $current_diff_path} {
|
if {$path eq $current_diff_path} {
|
||||||
set after {reshow_diff;}
|
$after_chord eval { set should_reshow_diff 1 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set path_cnt [llength $path_list]
|
||||||
|
set untracked_cnt [llength $untracked_list]
|
||||||
|
|
||||||
# Split question between singular and plural cases, because
|
# Asynchronous operation: revert changes by checking them out afresh
|
||||||
# such distinction is needed in some languages. Previously, the
|
# from the index.
|
||||||
# code used "Revert changes in" for both, but that can't work
|
if {$path_cnt > 0} {
|
||||||
# in languages where 'in' must be combined with word from
|
# Split question between singular and plural cases, because
|
||||||
# rest of string (in different way for both cases of course).
|
# such distinction is needed in some languages. Previously, the
|
||||||
#
|
# code used "Revert changes in" for both, but that can't work
|
||||||
# FIXME: Unfortunately, even that isn't enough in some languages
|
# in languages where 'in' must be combined with word from
|
||||||
# as they have quite complex plural-form rules. Unfortunately,
|
# rest of string (in different way for both cases of course).
|
||||||
# msgcat doesn't seem to support that kind of string translation.
|
#
|
||||||
#
|
# FIXME: Unfortunately, even that isn't enough in some languages
|
||||||
set n [llength $pathList]
|
# as they have quite complex plural-form rules. Unfortunately,
|
||||||
if {$n == 0} {
|
# msgcat doesn't seem to support that kind of string
|
||||||
unlock_index
|
# translation.
|
||||||
return
|
#
|
||||||
} elseif {$n == 1} {
|
if {$path_cnt == 1} {
|
||||||
set query [mc "Revert changes in file %s?" [short_path [lindex $pathList]]]
|
set query [mc \
|
||||||
} else {
|
"Revert changes in file %s?" \
|
||||||
set query [mc "Revert changes in these %i files?" $n]
|
[short_path [lindex $path_list]] \
|
||||||
}
|
]
|
||||||
|
} else {
|
||||||
|
set query [mc \
|
||||||
|
"Revert changes in these %i files?" \
|
||||||
|
$path_cnt]
|
||||||
|
}
|
||||||
|
|
||||||
set reply [tk_dialog \
|
set reply [tk_dialog \
|
||||||
.confirm_revert \
|
.confirm_revert \
|
||||||
"[appname] ([reponame])" \
|
"[appname] ([reponame])" \
|
||||||
"$query
|
"$query
|
||||||
|
|
||||||
[mc "Any unstaged changes will be permanently lost by the revert."]" \
|
[mc "Any unstaged changes will be permanently lost by the revert."]" \
|
||||||
question \
|
question \
|
||||||
1 \
|
1 \
|
||||||
[mc "Do Nothing"] \
|
[mc "Do Nothing"] \
|
||||||
[mc "Revert Changes"] \
|
[mc "Revert Changes"] \
|
||||||
]
|
]
|
||||||
if {$reply == 1} {
|
|
||||||
checkout_index \
|
if {$reply == 1} {
|
||||||
$txt \
|
checkout_index \
|
||||||
$pathList \
|
$txt \
|
||||||
[concat $after [list ui_ready]]
|
$path_list \
|
||||||
|
[$after_chord add_note] \
|
||||||
|
$capture_error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Asynchronous operation: Deletion of untracked files.
|
||||||
|
if {$untracked_cnt > 0} {
|
||||||
|
# Split question between singular and plural cases, because
|
||||||
|
# such distinction is needed in some languages.
|
||||||
|
#
|
||||||
|
# FIXME: Unfortunately, even that isn't enough in some languages
|
||||||
|
# as they have quite complex plural-form rules. Unfortunately,
|
||||||
|
# msgcat doesn't seem to support that kind of string
|
||||||
|
# translation.
|
||||||
|
#
|
||||||
|
if {$untracked_cnt == 1} {
|
||||||
|
set query [mc \
|
||||||
|
"Delete untracked file %s?" \
|
||||||
|
[short_path [lindex $untracked_list]] \
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
set query [mc \
|
||||||
|
"Delete these %i untracked files?" \
|
||||||
|
$untracked_cnt \
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
set reply [tk_dialog \
|
||||||
|
.confirm_revert \
|
||||||
|
"[appname] ([reponame])" \
|
||||||
|
"$query
|
||||||
|
|
||||||
|
[mc "Files will be permanently deleted."]" \
|
||||||
|
question \
|
||||||
|
1 \
|
||||||
|
[mc "Do Nothing"] \
|
||||||
|
[mc "Delete Files"] \
|
||||||
|
]
|
||||||
|
|
||||||
|
if {$reply == 1} {
|
||||||
|
$after_chord eval { set should_reshow_diff 1 }
|
||||||
|
|
||||||
|
delete_files $untracked_list [$after_chord add_note]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Activate the common note. If no other notes were created, this
|
||||||
|
# completes the chord. If other notes were created, then this common
|
||||||
|
# note prevents a race condition where the chord might complete early.
|
||||||
|
$after_common_note
|
||||||
|
}
|
||||||
|
|
||||||
|
# Delete all of the specified files, performing deletion in batches to allow the
|
||||||
|
# UI to remain responsive and updated.
|
||||||
|
proc delete_files {path_list after} {
|
||||||
|
# Enable progress bar status updates
|
||||||
|
set status_bar_operation [$::main_status \
|
||||||
|
start \
|
||||||
|
[mc "Deleting"] \
|
||||||
|
[mc "files"]]
|
||||||
|
|
||||||
|
set path_index 0
|
||||||
|
set deletion_errors [list]
|
||||||
|
set batch_size 50
|
||||||
|
|
||||||
|
delete_helper \
|
||||||
|
$path_list \
|
||||||
|
$path_index \
|
||||||
|
$deletion_errors \
|
||||||
|
$batch_size \
|
||||||
|
$status_bar_operation \
|
||||||
|
$after
|
||||||
|
}
|
||||||
|
|
||||||
|
# Helper function to delete a list of files in batches. Each call deletes one
|
||||||
|
# batch of files, and then schedules a call for the next batch after any UI
|
||||||
|
# messages have been processed.
|
||||||
|
proc delete_helper {path_list path_index deletion_errors batch_size \
|
||||||
|
status_bar_operation after} {
|
||||||
|
global file_states
|
||||||
|
|
||||||
|
set path_cnt [llength $path_list]
|
||||||
|
|
||||||
|
set batch_remaining $batch_size
|
||||||
|
|
||||||
|
while {$batch_remaining > 0} {
|
||||||
|
if {$path_index >= $path_cnt} { break }
|
||||||
|
|
||||||
|
set path [lindex $path_list $path_index]
|
||||||
|
|
||||||
|
set deletion_failed [catch {file delete -- $path} deletion_error]
|
||||||
|
|
||||||
|
if {$deletion_failed} {
|
||||||
|
lappend deletion_errors [list "$deletion_error"]
|
||||||
|
} else {
|
||||||
|
remove_empty_directories [file dirname $path]
|
||||||
|
|
||||||
|
# Don't assume the deletion worked. Remove the file from
|
||||||
|
# the UI, but only if it no longer exists.
|
||||||
|
if {![path_exists $path]} {
|
||||||
|
unset file_states($path)
|
||||||
|
display_file $path __
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
incr path_index 1
|
||||||
|
incr batch_remaining -1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update the progress bar to indicate that this batch has been
|
||||||
|
# completed. The update will be visible when this procedure returns
|
||||||
|
# and allows the UI thread to process messages.
|
||||||
|
$status_bar_operation update $path_index $path_cnt
|
||||||
|
|
||||||
|
if {$path_index < $path_cnt} {
|
||||||
|
# The Tcler's Wiki lists this as the best practice for keeping
|
||||||
|
# a UI active and processing messages during a long-running
|
||||||
|
# operation.
|
||||||
|
|
||||||
|
after idle [list after 0 [list \
|
||||||
|
delete_helper \
|
||||||
|
$path_list \
|
||||||
|
$path_index \
|
||||||
|
$deletion_errors \
|
||||||
|
$batch_size \
|
||||||
|
$status_bar_operation \
|
||||||
|
$after
|
||||||
|
]]
|
||||||
} else {
|
} else {
|
||||||
unlock_index
|
# Finish the status bar operation.
|
||||||
|
$status_bar_operation stop
|
||||||
|
|
||||||
|
# Report error, if any, based on how many deletions failed.
|
||||||
|
set deletion_error_cnt [llength $deletion_errors]
|
||||||
|
|
||||||
|
if {($deletion_error_cnt > 0)
|
||||||
|
&& ($deletion_error_cnt <= [MAX_VERBOSE_FILES_IN_DELETION_ERROR])} {
|
||||||
|
set error_text [mc "Encountered errors deleting files:\n"]
|
||||||
|
|
||||||
|
foreach deletion_error $deletion_errors {
|
||||||
|
append error_text "* [lindex $deletion_error 0]\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
error_popup $error_text
|
||||||
|
} elseif {$deletion_error_cnt == $path_cnt} {
|
||||||
|
error_popup [mc \
|
||||||
|
"None of the %d selected files could be deleted." \
|
||||||
|
$path_cnt \
|
||||||
|
]
|
||||||
|
} elseif {$deletion_error_cnt > 1} {
|
||||||
|
error_popup [mc \
|
||||||
|
"%d of the %d selected files could not be deleted." \
|
||||||
|
$deletion_error_cnt \
|
||||||
|
$path_cnt \
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
uplevel #0 $after
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proc MAX_VERBOSE_FILES_IN_DELETION_ERROR {} { return 10; }
|
||||||
|
|
||||||
|
# This function is from the TCL documentation:
|
||||||
|
#
|
||||||
|
# https://wiki.tcl-lang.org/page/file+exists
|
||||||
|
#
|
||||||
|
# [file exists] returns false if the path does exist but is a symlink to a path
|
||||||
|
# that doesn't exist. This proc returns true if the path exists, regardless of
|
||||||
|
# whether it is a symlink and whether it is broken.
|
||||||
|
proc path_exists {name} {
|
||||||
|
expr {![catch {file lstat $name finfo}]}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove as many empty directories as we can starting at the specified path,
|
||||||
|
# walking up the directory tree. If we encounter a directory that is not
|
||||||
|
# empty, or if a directory deletion fails, then we stop the operation and
|
||||||
|
# return to the caller. Even if this procedure fails to delete any
|
||||||
|
# directories at all, it does not report failure.
|
||||||
|
proc remove_empty_directories {directory_path} {
|
||||||
|
set parent_path [file dirname $directory_path]
|
||||||
|
|
||||||
|
while {$parent_path != $directory_path} {
|
||||||
|
set contents [glob -nocomplain -dir $directory_path *]
|
||||||
|
|
||||||
|
if {[llength $contents] > 0} { break }
|
||||||
|
if {[catch {file delete -- $directory_path}]} { break }
|
||||||
|
|
||||||
|
set directory_path $parent_path
|
||||||
|
set parent_path [file dirname $directory_path]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,23 +241,27 @@ Continue with resetting the current changes?"]
|
|||||||
if {[ask_popup $op_question] eq {yes}} {
|
if {[ask_popup $op_question] eq {yes}} {
|
||||||
set fd [git_read --stderr read-tree --reset -u -v HEAD]
|
set fd [git_read --stderr read-tree --reset -u -v HEAD]
|
||||||
fconfigure $fd -blocking 0 -translation binary
|
fconfigure $fd -blocking 0 -translation binary
|
||||||
fileevent $fd readable [namespace code [list _reset_wait $fd]]
|
set status_bar_operation [$::main_status \
|
||||||
$::main_status start [mc "Aborting"] [mc "files reset"]
|
start \
|
||||||
|
[mc "Aborting"] \
|
||||||
|
[mc "files reset"]
|
||||||
|
fileevent $fd readable [namespace code [list \
|
||||||
|
_reset_wait $fd $status_bar_operation]]
|
||||||
} else {
|
} else {
|
||||||
unlock_index
|
unlock_index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc _reset_wait {fd} {
|
proc _reset_wait {fd status_bar_operation} {
|
||||||
global ui_comm
|
global ui_comm
|
||||||
|
|
||||||
$::main_status update_meter [read $fd]
|
$status_bar_operation update_meter [read $fd]
|
||||||
|
|
||||||
fconfigure $fd -blocking 1
|
fconfigure $fd -blocking 1
|
||||||
if {[eof $fd]} {
|
if {[eof $fd]} {
|
||||||
set fail [catch {close $fd} err]
|
set fail [catch {close $fd} err]
|
||||||
$::main_status stop
|
|
||||||
unlock_index
|
unlock_index
|
||||||
|
$status_bar_operation stop
|
||||||
|
|
||||||
$ui_comm delete 0.0 end
|
$ui_comm delete 0.0 end
|
||||||
$ui_comm edit modified false
|
$ui_comm edit modified false
|
||||||
|
@ -1,16 +1,42 @@
|
|||||||
# git-gui status bar mega-widget
|
# git-gui status bar mega-widget
|
||||||
# Copyright (C) 2007 Shawn Pearce
|
# Copyright (C) 2007 Shawn Pearce
|
||||||
|
|
||||||
|
# The status_bar class manages the entire status bar. It is possible for
|
||||||
|
# multiple overlapping asynchronous operations to want to display status
|
||||||
|
# simultaneously. Each one receives a status_bar_operation when it calls the
|
||||||
|
# start method, and the status bar combines all active operations into the
|
||||||
|
# line of text it displays. Most of the time, there will be at most one
|
||||||
|
# ongoing operation.
|
||||||
|
#
|
||||||
|
# Note that the entire status bar can be either in single-line or two-line
|
||||||
|
# mode, depending on the constructor. Multiple active operations are only
|
||||||
|
# supported for single-line status bars.
|
||||||
|
|
||||||
class status_bar {
|
class status_bar {
|
||||||
|
|
||||||
|
field allow_multiple ; # configured at construction
|
||||||
|
|
||||||
field w ; # our own window path
|
field w ; # our own window path
|
||||||
field w_l ; # text widget we draw messages into
|
field w_l ; # text widget we draw messages into
|
||||||
field w_c ; # canvas we draw a progress bar into
|
field w_c ; # canvas we draw a progress bar into
|
||||||
field c_pack ; # script to pack the canvas with
|
field c_pack ; # script to pack the canvas with
|
||||||
field status {}; # single line of text we show
|
|
||||||
field prefix {}; # text we format into status
|
field baseline_text ; # text to show if there are no operations
|
||||||
field units {}; # unit of progress
|
field status_bar_text ; # combined text for all operations
|
||||||
field meter {}; # current core git progress meter (if active)
|
|
||||||
|
field operations ; # list of current ongoing operations
|
||||||
|
|
||||||
|
# The status bar can display a progress bar, updated when consumers call the
|
||||||
|
# update method on their status_bar_operation. When there are multiple
|
||||||
|
# operations, the status bar shows the combined status of all operations.
|
||||||
|
#
|
||||||
|
# When an overlapping operation completes, the progress bar is going to
|
||||||
|
# abruptly have one fewer operation in the calculation, causing a discontinuity.
|
||||||
|
# Therefore, whenever an operation completes, if it is not the last operation,
|
||||||
|
# this counter is increased, and the progress bar is calculated as though there
|
||||||
|
# were still another operation at 100%. When the last operation completes, this
|
||||||
|
# is reset to 0.
|
||||||
|
field completed_operation_count
|
||||||
|
|
||||||
constructor new {path} {
|
constructor new {path} {
|
||||||
global use_ttk NS
|
global use_ttk NS
|
||||||
@ -18,12 +44,19 @@ constructor new {path} {
|
|||||||
set w_l $w.l
|
set w_l $w.l
|
||||||
set w_c $w.c
|
set w_c $w.c
|
||||||
|
|
||||||
|
# Standard single-line status bar: Permit overlapping operations
|
||||||
|
set allow_multiple 1
|
||||||
|
|
||||||
|
set baseline_text ""
|
||||||
|
set operations [list]
|
||||||
|
set completed_operation_count 0
|
||||||
|
|
||||||
${NS}::frame $w
|
${NS}::frame $w
|
||||||
if {!$use_ttk} {
|
if {!$use_ttk} {
|
||||||
$w configure -borderwidth 1 -relief sunken
|
$w configure -borderwidth 1 -relief sunken
|
||||||
}
|
}
|
||||||
${NS}::label $w_l \
|
${NS}::label $w_l \
|
||||||
-textvariable @status \
|
-textvariable @status_bar_text \
|
||||||
-anchor w \
|
-anchor w \
|
||||||
-justify left
|
-justify left
|
||||||
pack $w_l -side left
|
pack $w_l -side left
|
||||||
@ -44,9 +77,16 @@ constructor two_line {path} {
|
|||||||
set w_l $w.l
|
set w_l $w.l
|
||||||
set w_c $w.c
|
set w_c $w.c
|
||||||
|
|
||||||
|
# Two-line status bar: Only one ongoing operation permitted.
|
||||||
|
set allow_multiple 0
|
||||||
|
|
||||||
|
set baseline_text ""
|
||||||
|
set operations [list]
|
||||||
|
set completed_operation_count 0
|
||||||
|
|
||||||
${NS}::frame $w
|
${NS}::frame $w
|
||||||
${NS}::label $w_l \
|
${NS}::label $w_l \
|
||||||
-textvariable @status \
|
-textvariable @status_bar_text \
|
||||||
-anchor w \
|
-anchor w \
|
||||||
-justify left
|
-justify left
|
||||||
pack $w_l -anchor w -fill x
|
pack $w_l -anchor w -fill x
|
||||||
@ -56,7 +96,7 @@ constructor two_line {path} {
|
|||||||
return $this
|
return $this
|
||||||
}
|
}
|
||||||
|
|
||||||
method start {msg uds} {
|
method ensure_canvas {} {
|
||||||
if {[winfo exists $w_c]} {
|
if {[winfo exists $w_c]} {
|
||||||
$w_c coords bar 0 0 0 20
|
$w_c coords bar 0 0 0 20
|
||||||
} else {
|
} else {
|
||||||
@ -68,31 +108,170 @@ method start {msg uds} {
|
|||||||
$w_c create rectangle 0 0 0 20 -tags bar -fill navy
|
$w_c create rectangle 0 0 0 20 -tags bar -fill navy
|
||||||
eval $c_pack
|
eval $c_pack
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
method show {msg} {
|
||||||
|
$this ensure_canvas
|
||||||
|
set baseline_text $msg
|
||||||
|
$this refresh
|
||||||
|
}
|
||||||
|
|
||||||
|
method start {msg {uds {}}} {
|
||||||
|
set baseline_text ""
|
||||||
|
|
||||||
|
if {!$allow_multiple && [llength $operations]} {
|
||||||
|
return [lindex $operations 0]
|
||||||
|
}
|
||||||
|
|
||||||
|
$this ensure_canvas
|
||||||
|
|
||||||
|
set operation [status_bar_operation::new $this $msg $uds]
|
||||||
|
|
||||||
|
lappend operations $operation
|
||||||
|
|
||||||
|
$this refresh
|
||||||
|
|
||||||
|
return $operation
|
||||||
|
}
|
||||||
|
|
||||||
|
method refresh {} {
|
||||||
|
set new_text ""
|
||||||
|
|
||||||
|
set total [expr $completed_operation_count * 100]
|
||||||
|
set have $total
|
||||||
|
|
||||||
|
foreach operation $operations {
|
||||||
|
if {$new_text != ""} {
|
||||||
|
append new_text " / "
|
||||||
|
}
|
||||||
|
|
||||||
|
append new_text [$operation get_status]
|
||||||
|
|
||||||
|
set total [expr $total + 100]
|
||||||
|
set have [expr $have + [$operation get_progress]]
|
||||||
|
}
|
||||||
|
|
||||||
|
if {$new_text == ""} {
|
||||||
|
set new_text $baseline_text
|
||||||
|
}
|
||||||
|
|
||||||
|
set status_bar_text $new_text
|
||||||
|
|
||||||
|
if {[winfo exists $w_c]} {
|
||||||
|
set pixel_width 0
|
||||||
|
if {$have > 0} {
|
||||||
|
set pixel_width [expr {[winfo width $w_c] * $have / $total}]
|
||||||
|
}
|
||||||
|
|
||||||
|
$w_c coords bar 0 0 $pixel_width 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
method stop {operation stop_msg} {
|
||||||
|
set idx [lsearch $operations $operation]
|
||||||
|
|
||||||
|
if {$idx >= 0} {
|
||||||
|
set operations [lreplace $operations $idx $idx]
|
||||||
|
set completed_operation_count [expr \
|
||||||
|
$completed_operation_count + 1]
|
||||||
|
|
||||||
|
if {[llength $operations] == 0} {
|
||||||
|
set completed_operation_count 0
|
||||||
|
|
||||||
|
destroy $w_c
|
||||||
|
if {$stop_msg ne {}} {
|
||||||
|
set baseline_text $stop_msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this refresh
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
method stop_all {{stop_msg {}}} {
|
||||||
|
# This makes the operation's call to stop a no-op.
|
||||||
|
set operations_copy $operations
|
||||||
|
set operations [list]
|
||||||
|
|
||||||
|
foreach operation $operations_copy {
|
||||||
|
$operation stop
|
||||||
|
}
|
||||||
|
|
||||||
|
if {$stop_msg ne {}} {
|
||||||
|
set baseline_text $stop_msg
|
||||||
|
}
|
||||||
|
|
||||||
|
$this refresh
|
||||||
|
}
|
||||||
|
|
||||||
|
method _delete {current} {
|
||||||
|
if {$current eq $w} {
|
||||||
|
delete_this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# The status_bar_operation class tracks a single consumer's ongoing status bar
|
||||||
|
# activity, with the context that there are a few situations where multiple
|
||||||
|
# overlapping asynchronous operations might want to display status information
|
||||||
|
# simultaneously. Instances of status_bar_operation are created by calling
|
||||||
|
# start on the status_bar, and when the caller is done with its stauts bar
|
||||||
|
# operation, it calls stop on the operation.
|
||||||
|
|
||||||
|
class status_bar_operation {
|
||||||
|
|
||||||
|
field status_bar; # reference back to the status_bar that owns this object
|
||||||
|
|
||||||
|
field is_active;
|
||||||
|
|
||||||
|
field status {}; # single line of text we show
|
||||||
|
field progress {}; # current progress (0 to 100)
|
||||||
|
field prefix {}; # text we format into status
|
||||||
|
field units {}; # unit of progress
|
||||||
|
field meter {}; # current core git progress meter (if active)
|
||||||
|
|
||||||
|
constructor new {owner msg uds} {
|
||||||
|
set status_bar $owner
|
||||||
|
|
||||||
set status $msg
|
set status $msg
|
||||||
|
set progress 0
|
||||||
set prefix $msg
|
set prefix $msg
|
||||||
set units $uds
|
set units $uds
|
||||||
set meter {}
|
set meter {}
|
||||||
|
|
||||||
|
set is_active 1
|
||||||
|
|
||||||
|
return $this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
method get_is_active {} { return $is_active }
|
||||||
|
method get_status {} { return $status }
|
||||||
|
method get_progress {} { return $progress }
|
||||||
|
|
||||||
method update {have total} {
|
method update {have total} {
|
||||||
set pdone 0
|
if {!$is_active} { return }
|
||||||
set cdone 0
|
|
||||||
|
set progress 0
|
||||||
|
|
||||||
if {$total > 0} {
|
if {$total > 0} {
|
||||||
set pdone [expr {100 * $have / $total}]
|
set progress [expr {100 * $have / $total}]
|
||||||
set cdone [expr {[winfo width $w_c] * $have / $total}]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set prec [string length [format %i $total]]
|
set prec [string length [format %i $total]]
|
||||||
|
|
||||||
set status [mc "%s ... %*i of %*i %s (%3i%%)" \
|
set status [mc "%s ... %*i of %*i %s (%3i%%)" \
|
||||||
$prefix \
|
$prefix \
|
||||||
$prec $have \
|
$prec $have \
|
||||||
$prec $total \
|
$prec $total \
|
||||||
$units $pdone]
|
$units $progress]
|
||||||
$w_c coords bar 0 0 $cdone 20
|
|
||||||
|
$status_bar refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
method update_meter {buf} {
|
method update_meter {buf} {
|
||||||
|
if {!$is_active} { return }
|
||||||
|
|
||||||
append meter $buf
|
append meter $buf
|
||||||
set r [string last "\r" $meter]
|
set r [string last "\r" $meter]
|
||||||
if {$r == -1} {
|
if {$r == -1} {
|
||||||
@ -109,23 +288,25 @@ method update_meter {buf} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
method stop {{msg {}}} {
|
method stop {{stop_msg {}}} {
|
||||||
destroy $w_c
|
if {$is_active} {
|
||||||
if {$msg ne {}} {
|
set is_active 0
|
||||||
set status $msg
|
$status_bar stop $this $stop_msg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
method show {msg {test {}}} {
|
method restart {msg} {
|
||||||
if {$test eq {} || $status eq $test} {
|
if {!$is_active} { return }
|
||||||
set status $msg
|
|
||||||
}
|
set status $msg
|
||||||
|
set prefix $msg
|
||||||
|
set meter {}
|
||||||
|
$status_bar refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
method _delete {current} {
|
method _delete {} {
|
||||||
if {$current eq $w} {
|
stop
|
||||||
delete_this
|
delete_this
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -741,7 +741,7 @@ sub evaluate_gitweb_config {
|
|||||||
$GITWEB_CONFIG_SYSTEM = "" if ($GITWEB_CONFIG_SYSTEM eq $GITWEB_CONFIG_COMMON);
|
$GITWEB_CONFIG_SYSTEM = "" if ($GITWEB_CONFIG_SYSTEM eq $GITWEB_CONFIG_COMMON);
|
||||||
|
|
||||||
# Common system-wide settings for convenience.
|
# Common system-wide settings for convenience.
|
||||||
# Those settings can be ovverriden by GITWEB_CONFIG or GITWEB_CONFIG_SYSTEM.
|
# Those settings can be overridden by GITWEB_CONFIG or GITWEB_CONFIG_SYSTEM.
|
||||||
read_config_file($GITWEB_CONFIG_COMMON);
|
read_config_file($GITWEB_CONFIG_COMMON);
|
||||||
|
|
||||||
# Use first config file that exists. This means use the per-instance
|
# Use first config file that exists. This means use the per-instance
|
||||||
@ -5285,7 +5285,7 @@ sub format_ctx_rem_add_lines {
|
|||||||
# + c
|
# + c
|
||||||
# + d
|
# + d
|
||||||
#
|
#
|
||||||
# Otherwise the highlightling would be confusing.
|
# Otherwise the highlighting would be confusing.
|
||||||
if ($is_combined) {
|
if ($is_combined) {
|
||||||
for (my $i = 0; $i < @$add; $i++) {
|
for (my $i = 0; $i < @$add; $i++) {
|
||||||
my $prefix_rem = substr($rem->[$i], 0, $num_parents);
|
my $prefix_rem = substr($rem->[$i], 0, $num_parents);
|
||||||
|
17
graph.c
17
graph.c
@ -1063,7 +1063,7 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct graph_l
|
|||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
struct commit_list *first_parent = first_interesting_parent(graph);
|
struct commit_list *first_parent = first_interesting_parent(graph);
|
||||||
int seen_parent = 0;
|
struct column *parent_col = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Output the post-merge row
|
* Output the post-merge row
|
||||||
@ -1117,12 +1117,17 @@ static void graph_output_post_merge_line(struct git_graph *graph, struct graph_l
|
|||||||
graph_line_addch(line, ' ');
|
graph_line_addch(line, ' ');
|
||||||
} else {
|
} else {
|
||||||
graph_line_write_column(line, col, '|');
|
graph_line_write_column(line, col, '|');
|
||||||
if (graph->merge_layout != 0 || i != graph->commit_index - 1)
|
if (graph->merge_layout != 0 || i != graph->commit_index - 1) {
|
||||||
graph_line_addch(line, seen_parent ? '_' : ' ');
|
if (parent_col)
|
||||||
|
graph_line_write_column(
|
||||||
|
line, parent_col, '_');
|
||||||
|
else
|
||||||
|
graph_line_addch(line, ' ');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (col_commit == first_parent->item)
|
if (col_commit == first_parent->item)
|
||||||
seen_parent = 1;
|
parent_col = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1219,13 +1224,9 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct graph_l
|
|||||||
*
|
*
|
||||||
* The space just to the left of this
|
* The space just to the left of this
|
||||||
* branch should always be empty.
|
* branch should always be empty.
|
||||||
*
|
|
||||||
* The branch to the left of that space
|
|
||||||
* should be our eventual target.
|
|
||||||
*/
|
*/
|
||||||
assert(graph->mapping[i - 1] > target);
|
assert(graph->mapping[i - 1] > target);
|
||||||
assert(graph->mapping[i - 2] < 0);
|
assert(graph->mapping[i - 2] < 0);
|
||||||
assert(graph->mapping[i - 3] == target);
|
|
||||||
graph->mapping[i - 2] = target;
|
graph->mapping[i - 2] = target;
|
||||||
/*
|
/*
|
||||||
* Mark this branch as the horizontal edge to
|
* Mark this branch as the horizontal edge to
|
||||||
|
@ -224,17 +224,6 @@ static struct commit *make_virtual_commit(struct repository *repo,
|
|||||||
return commit;
|
return commit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Since we use get_tree_entry(), which does not put the read object into
|
|
||||||
* the object pool, we cannot rely on a == b.
|
|
||||||
*/
|
|
||||||
static int oid_eq(const struct object_id *a, const struct object_id *b)
|
|
||||||
{
|
|
||||||
if (!a && !b)
|
|
||||||
return 2;
|
|
||||||
return a && b && oideq(a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum rename_type {
|
enum rename_type {
|
||||||
RENAME_NORMAL = 0,
|
RENAME_NORMAL = 0,
|
||||||
RENAME_VIA_DIR,
|
RENAME_VIA_DIR,
|
||||||
@ -805,7 +794,7 @@ static int was_tracked_and_matches(struct merge_options *opt, const char *path,
|
|||||||
|
|
||||||
/* See if the file we were tracking before matches */
|
/* See if the file we were tracking before matches */
|
||||||
ce = opt->priv->orig_index.cache[pos];
|
ce = opt->priv->orig_index.cache[pos];
|
||||||
return (oid_eq(&ce->oid, &blob->oid) && ce->ce_mode == blob->mode);
|
return (oideq(&ce->oid, &blob->oid) && ce->ce_mode == blob->mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1317,7 +1306,7 @@ static int merge_mode_and_contents(struct merge_options *opt,
|
|||||||
oidcpy(&result->blob.oid, &b->oid);
|
oidcpy(&result->blob.oid, &b->oid);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!oid_eq(&a->oid, &o->oid) && !oid_eq(&b->oid, &o->oid))
|
if (!oideq(&a->oid, &o->oid) && !oideq(&b->oid, &o->oid))
|
||||||
result->merge = 1;
|
result->merge = 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1333,9 +1322,9 @@ static int merge_mode_and_contents(struct merge_options *opt,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &o->oid))
|
if (oideq(&a->oid, &b->oid) || oideq(&a->oid, &o->oid))
|
||||||
oidcpy(&result->blob.oid, &b->oid);
|
oidcpy(&result->blob.oid, &b->oid);
|
||||||
else if (oid_eq(&b->oid, &o->oid))
|
else if (oideq(&b->oid, &o->oid))
|
||||||
oidcpy(&result->blob.oid, &a->oid);
|
oidcpy(&result->blob.oid, &a->oid);
|
||||||
else if (S_ISREG(a->mode)) {
|
else if (S_ISREG(a->mode)) {
|
||||||
mmbuffer_t result_buf;
|
mmbuffer_t result_buf;
|
||||||
@ -1368,7 +1357,7 @@ static int merge_mode_and_contents(struct merge_options *opt,
|
|||||||
switch (opt->recursive_variant) {
|
switch (opt->recursive_variant) {
|
||||||
case MERGE_VARIANT_NORMAL:
|
case MERGE_VARIANT_NORMAL:
|
||||||
oidcpy(&result->blob.oid, &a->oid);
|
oidcpy(&result->blob.oid, &a->oid);
|
||||||
if (!oid_eq(&a->oid, &b->oid))
|
if (!oideq(&a->oid, &b->oid))
|
||||||
result->clean = 0;
|
result->clean = 0;
|
||||||
break;
|
break;
|
||||||
case MERGE_VARIANT_OURS:
|
case MERGE_VARIANT_OURS:
|
||||||
@ -2836,15 +2825,15 @@ static int process_renames(struct merge_options *opt,
|
|||||||
dst_other.mode = ren1->dst_entry->stages[other_stage].mode;
|
dst_other.mode = ren1->dst_entry->stages[other_stage].mode;
|
||||||
try_merge = 0;
|
try_merge = 0;
|
||||||
|
|
||||||
if (oid_eq(&src_other.oid, &null_oid) &&
|
if (oideq(&src_other.oid, &null_oid) &&
|
||||||
ren1->dir_rename_original_type == 'A') {
|
ren1->dir_rename_original_type == 'A') {
|
||||||
setup_rename_conflict_info(RENAME_VIA_DIR,
|
setup_rename_conflict_info(RENAME_VIA_DIR,
|
||||||
opt, ren1, NULL);
|
opt, ren1, NULL);
|
||||||
} else if (oid_eq(&src_other.oid, &null_oid)) {
|
} else if (oideq(&src_other.oid, &null_oid)) {
|
||||||
setup_rename_conflict_info(RENAME_DELETE,
|
setup_rename_conflict_info(RENAME_DELETE,
|
||||||
opt, ren1, NULL);
|
opt, ren1, NULL);
|
||||||
} else if ((dst_other.mode == ren1->pair->two->mode) &&
|
} else if ((dst_other.mode == ren1->pair->two->mode) &&
|
||||||
oid_eq(&dst_other.oid, &ren1->pair->two->oid)) {
|
oideq(&dst_other.oid, &ren1->pair->two->oid)) {
|
||||||
/*
|
/*
|
||||||
* Added file on the other side identical to
|
* Added file on the other side identical to
|
||||||
* the file being renamed: clean merge.
|
* the file being renamed: clean merge.
|
||||||
@ -2859,7 +2848,7 @@ static int process_renames(struct merge_options *opt,
|
|||||||
1, /* update_cache */
|
1, /* update_cache */
|
||||||
0 /* update_wd */))
|
0 /* update_wd */))
|
||||||
clean_merge = -1;
|
clean_merge = -1;
|
||||||
} else if (!oid_eq(&dst_other.oid, &null_oid)) {
|
} else if (!oideq(&dst_other.oid, &null_oid)) {
|
||||||
/*
|
/*
|
||||||
* Probably not a clean merge, but it's
|
* Probably not a clean merge, but it's
|
||||||
* premature to set clean_merge to 0 here,
|
* premature to set clean_merge to 0 here,
|
||||||
@ -3037,7 +3026,7 @@ static int blob_unchanged(struct merge_options *opt,
|
|||||||
|
|
||||||
if (a->mode != o->mode)
|
if (a->mode != o->mode)
|
||||||
return 0;
|
return 0;
|
||||||
if (oid_eq(&o->oid, &a->oid))
|
if (oideq(&o->oid, &a->oid))
|
||||||
return 1;
|
return 1;
|
||||||
if (!renormalize)
|
if (!renormalize)
|
||||||
return 0;
|
return 0;
|
||||||
@ -3478,7 +3467,7 @@ static int merge_trees_internal(struct merge_options *opt,
|
|||||||
opt->subtree_shift);
|
opt->subtree_shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oid_eq(&merge_base->object.oid, &merge->object.oid)) {
|
if (oideq(&merge_base->object.oid, &merge->object.oid)) {
|
||||||
output(opt, 0, _("Already up to date!"));
|
output(opt, 0, _("Already up to date!"));
|
||||||
*result = head;
|
*result = head;
|
||||||
return 1;
|
return 1;
|
||||||
|
16
packfile.c
16
packfile.c
@ -510,7 +510,6 @@ static int open_packed_git_1(struct packed_git *p)
|
|||||||
struct pack_header hdr;
|
struct pack_header hdr;
|
||||||
unsigned char hash[GIT_MAX_RAWSZ];
|
unsigned char hash[GIT_MAX_RAWSZ];
|
||||||
unsigned char *idx_hash;
|
unsigned char *idx_hash;
|
||||||
long fd_flag;
|
|
||||||
ssize_t read_result;
|
ssize_t read_result;
|
||||||
const unsigned hashsz = the_hash_algo->rawsz;
|
const unsigned hashsz = the_hash_algo->rawsz;
|
||||||
|
|
||||||
@ -554,16 +553,6 @@ static int open_packed_git_1(struct packed_git *p)
|
|||||||
} else if (p->pack_size != st.st_size)
|
} else if (p->pack_size != st.st_size)
|
||||||
return error("packfile %s size changed", p->pack_name);
|
return error("packfile %s size changed", p->pack_name);
|
||||||
|
|
||||||
/* We leave these file descriptors open with sliding mmap;
|
|
||||||
* there is no point keeping them open across exec(), though.
|
|
||||||
*/
|
|
||||||
fd_flag = fcntl(p->pack_fd, F_GETFD, 0);
|
|
||||||
if (fd_flag < 0)
|
|
||||||
return error("cannot determine file descriptor flags");
|
|
||||||
fd_flag |= FD_CLOEXEC;
|
|
||||||
if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1)
|
|
||||||
return error("cannot set FD_CLOEXEC");
|
|
||||||
|
|
||||||
/* Verify we recognize this pack file format. */
|
/* Verify we recognize this pack file format. */
|
||||||
read_result = read_in_full(p->pack_fd, &hdr, sizeof(hdr));
|
read_result = read_in_full(p->pack_fd, &hdr, sizeof(hdr));
|
||||||
if (read_result < 0)
|
if (read_result < 0)
|
||||||
@ -587,9 +576,8 @@ static int open_packed_git_1(struct packed_git *p)
|
|||||||
" while index indicates %"PRIu32" objects",
|
" while index indicates %"PRIu32" objects",
|
||||||
p->pack_name, ntohl(hdr.hdr_entries),
|
p->pack_name, ntohl(hdr.hdr_entries),
|
||||||
p->num_objects);
|
p->num_objects);
|
||||||
if (lseek(p->pack_fd, p->pack_size - hashsz, SEEK_SET) == -1)
|
read_result = pread_in_full(p->pack_fd, hash, hashsz,
|
||||||
return error("end of packfile %s is unavailable", p->pack_name);
|
p->pack_size - hashsz);
|
||||||
read_result = read_in_full(p->pack_fd, hash, hashsz);
|
|
||||||
if (read_result < 0)
|
if (read_result < 0)
|
||||||
return error_errno("error reading from %s", p->pack_name);
|
return error_errno("error reading from %s", p->pack_name);
|
||||||
if (read_result != hashsz)
|
if (read_result != hashsz)
|
||||||
|
5
po/TEAMS
5
po/TEAMS
@ -67,3 +67,8 @@ Leader: Jiang Xin <worldhello.net AT gmail.com>
|
|||||||
Members: Ray Chen <oldsharp AT gmail.com>
|
Members: Ray Chen <oldsharp AT gmail.com>
|
||||||
依云 <lilydjwg AT gmail.com>
|
依云 <lilydjwg AT gmail.com>
|
||||||
Fangyi Zhou <me AT fangyi.io>
|
Fangyi Zhou <me AT fangyi.io>
|
||||||
|
|
||||||
|
Language: zh_TW (Traditional Chinese)
|
||||||
|
Respository: https://github.com/l10n-tw/git-po
|
||||||
|
Leader: Yi-Jyun Pan <pan93412 AT gmail.com>
|
||||||
|
Members: Franklin Weng <franklin AT goodhorse.idv.tw>
|
||||||
|
5025
po/git.pot
5025
po/git.pot
File diff suppressed because it is too large
Load Diff
5082
po/zh_CN.po
5082
po/zh_CN.po
File diff suppressed because it is too large
Load Diff
24000
po/zh_TW.po
Normal file
24000
po/zh_TW.po
Normal file
File diff suppressed because it is too large
Load Diff
@ -959,7 +959,7 @@ static int verify_dotfile(const char *rest, unsigned mode)
|
|||||||
|
|
||||||
int verify_path(const char *path, unsigned mode)
|
int verify_path(const char *path, unsigned mode)
|
||||||
{
|
{
|
||||||
char c;
|
char c = 0;
|
||||||
|
|
||||||
if (has_dos_drive_prefix(path))
|
if (has_dos_drive_prefix(path))
|
||||||
return 0;
|
return 0;
|
||||||
@ -974,6 +974,7 @@ int verify_path(const char *path, unsigned mode)
|
|||||||
if (is_dir_sep(c)) {
|
if (is_dir_sep(c)) {
|
||||||
inside:
|
inside:
|
||||||
if (protect_hfs) {
|
if (protect_hfs) {
|
||||||
|
|
||||||
if (is_hfs_dotgit(path))
|
if (is_hfs_dotgit(path))
|
||||||
return 0;
|
return 0;
|
||||||
if (S_ISLNK(mode)) {
|
if (S_ISLNK(mode)) {
|
||||||
@ -982,6 +983,10 @@ inside:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (protect_ntfs) {
|
if (protect_ntfs) {
|
||||||
|
#ifdef GIT_WINDOWS_NATIVE
|
||||||
|
if (c == '\\')
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
if (is_ntfs_dotgit(path))
|
if (is_ntfs_dotgit(path))
|
||||||
return 0;
|
return 0;
|
||||||
if (S_ISLNK(mode)) {
|
if (S_ISLNK(mode)) {
|
||||||
|
141
sequencer.c
141
sequencer.c
@ -147,8 +147,6 @@ static GIT_PATH_FUNC(rebase_path_refs_to_delete, "rebase-merge/refs-to-delete")
|
|||||||
* command-line.
|
* command-line.
|
||||||
*/
|
*/
|
||||||
static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
|
static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, "rebase-merge/gpg_sign_opt")
|
||||||
static GIT_PATH_FUNC(rebase_path_cdate_is_adate, "rebase-merge/cdate_is_adate")
|
|
||||||
static GIT_PATH_FUNC(rebase_path_ignore_date, "rebase-merge/ignore_date")
|
|
||||||
static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head")
|
static GIT_PATH_FUNC(rebase_path_orig_head, "rebase-merge/orig-head")
|
||||||
static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose")
|
static GIT_PATH_FUNC(rebase_path_verbose, "rebase-merge/verbose")
|
||||||
static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet")
|
static GIT_PATH_FUNC(rebase_path_quiet, "rebase-merge/quiet")
|
||||||
@ -825,19 +823,9 @@ int read_author_script(const char *path, char **name, char **email, char **date,
|
|||||||
error(_("missing 'GIT_AUTHOR_DATE'"));
|
error(_("missing 'GIT_AUTHOR_DATE'"));
|
||||||
if (date_i < 0 || email_i < 0 || date_i < 0 || err)
|
if (date_i < 0 || email_i < 0 || date_i < 0 || err)
|
||||||
goto finish;
|
goto finish;
|
||||||
|
*name = kv.items[name_i].util;
|
||||||
if (name)
|
*email = kv.items[email_i].util;
|
||||||
*name = kv.items[name_i].util;
|
*date = kv.items[date_i].util;
|
||||||
else
|
|
||||||
free(kv.items[name_i].util);
|
|
||||||
if (email)
|
|
||||||
*email = kv.items[email_i].util;
|
|
||||||
else
|
|
||||||
free(kv.items[email_i].util);
|
|
||||||
if (date)
|
|
||||||
*date = kv.items[date_i].util;
|
|
||||||
else
|
|
||||||
free(kv.items[date_i].util);
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
finish:
|
finish:
|
||||||
string_list_clear(&kv, !!retval);
|
string_list_clear(&kv, !!retval);
|
||||||
@ -880,47 +868,6 @@ static char *get_author(const char *message)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns a "date" string that needs to be free()'d by the caller */
|
|
||||||
static char *read_author_date_or_null(void)
|
|
||||||
{
|
|
||||||
char *date;
|
|
||||||
|
|
||||||
if (read_author_script(rebase_path_author_script(),
|
|
||||||
NULL, NULL, &date, 0))
|
|
||||||
return NULL;
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Construct a free()able author string with current time as the author date */
|
|
||||||
static char *ignore_author_date(const char *author)
|
|
||||||
{
|
|
||||||
int len = strlen(author);
|
|
||||||
struct ident_split ident;
|
|
||||||
struct strbuf new_author = STRBUF_INIT;
|
|
||||||
|
|
||||||
if (split_ident_line(&ident, author, len) < 0) {
|
|
||||||
error(_("malformed ident line"));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
len = ident.mail_end - ident.name_begin + 1;
|
|
||||||
|
|
||||||
strbuf_addf(&new_author, "%.*s ", len, ident.name_begin);
|
|
||||||
datestamp(&new_author);
|
|
||||||
return strbuf_detach(&new_author, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void push_dates(struct child_process *child, int change_committer_date)
|
|
||||||
{
|
|
||||||
time_t now = time(NULL);
|
|
||||||
struct strbuf date = STRBUF_INIT;
|
|
||||||
|
|
||||||
strbuf_addf(&date, "@%"PRIuMAX, (uintmax_t)now);
|
|
||||||
argv_array_pushf(&child->env_array, "GIT_AUTHOR_DATE=%s", date.buf);
|
|
||||||
if (change_committer_date)
|
|
||||||
argv_array_pushf(&child->env_array, "GIT_COMMITTER_DATE=%s", date.buf);
|
|
||||||
strbuf_release(&date);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char staged_changes_advice[] =
|
static const char staged_changes_advice[] =
|
||||||
N_("you have staged changes in your working tree\n"
|
N_("you have staged changes in your working tree\n"
|
||||||
"If these changes are meant to be squashed into the previous commit, run:\n"
|
"If these changes are meant to be squashed into the previous commit, run:\n"
|
||||||
@ -980,25 +927,6 @@ static int run_git_commit(struct repository *r,
|
|||||||
|
|
||||||
cmd.git_cmd = 1;
|
cmd.git_cmd = 1;
|
||||||
|
|
||||||
if (opts->committer_date_is_author_date) {
|
|
||||||
int res = -1;
|
|
||||||
struct strbuf datebuf = STRBUF_INIT;
|
|
||||||
char *date = read_author_date_or_null();
|
|
||||||
|
|
||||||
if (!date)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
strbuf_addf(&datebuf, "@%s", date);
|
|
||||||
res = setenv("GIT_COMMITTER_DATE",
|
|
||||||
opts->ignore_date ? "" : datebuf.buf, 1);
|
|
||||||
|
|
||||||
strbuf_release(&datebuf);
|
|
||||||
free(date);
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_rebase_i(opts) && read_env_script(&cmd.env_array)) {
|
if (is_rebase_i(opts) && read_env_script(&cmd.env_array)) {
|
||||||
const char *gpg_opt = gpg_sign_opt_quoted(opts);
|
const char *gpg_opt = gpg_sign_opt_quoted(opts);
|
||||||
|
|
||||||
@ -1014,8 +942,6 @@ static int run_git_commit(struct repository *r,
|
|||||||
argv_array_push(&cmd.args, "--amend");
|
argv_array_push(&cmd.args, "--amend");
|
||||||
if (opts->gpg_sign)
|
if (opts->gpg_sign)
|
||||||
argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
|
argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
|
||||||
if (opts->ignore_date)
|
|
||||||
push_dates(&cmd, opts->committer_date_is_author_date);
|
|
||||||
if (defmsg)
|
if (defmsg)
|
||||||
argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
|
argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
|
||||||
else if (!(flags & EDIT_MSG))
|
else if (!(flags & EDIT_MSG))
|
||||||
@ -1384,13 +1310,14 @@ static int try_to_commit(struct repository *r,
|
|||||||
struct commit_extra_header *extra = NULL;
|
struct commit_extra_header *extra = NULL;
|
||||||
struct strbuf err = STRBUF_INIT;
|
struct strbuf err = STRBUF_INIT;
|
||||||
struct strbuf commit_msg = STRBUF_INIT;
|
struct strbuf commit_msg = STRBUF_INIT;
|
||||||
char *author_to_free = NULL;
|
char *amend_author = NULL;
|
||||||
const char *hook_commit = NULL;
|
const char *hook_commit = NULL;
|
||||||
enum commit_msg_cleanup_mode cleanup;
|
enum commit_msg_cleanup_mode cleanup;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
if (parse_head(r, ¤t_head))
|
if (parse_head(r, ¤t_head))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (flags & AMEND_MSG) {
|
if (flags & AMEND_MSG) {
|
||||||
const char *exclude_gpgsig[] = { "gpgsig", NULL };
|
const char *exclude_gpgsig[] = { "gpgsig", NULL };
|
||||||
const char *out_enc = get_commit_output_encoding();
|
const char *out_enc = get_commit_output_encoding();
|
||||||
@ -1405,7 +1332,7 @@ static int try_to_commit(struct repository *r,
|
|||||||
strbuf_addstr(msg, orig_message);
|
strbuf_addstr(msg, orig_message);
|
||||||
hook_commit = "HEAD";
|
hook_commit = "HEAD";
|
||||||
}
|
}
|
||||||
author = author_to_free = get_author(message);
|
author = amend_author = get_author(message);
|
||||||
unuse_commit_buffer(current_head, message);
|
unuse_commit_buffer(current_head, message);
|
||||||
if (!author) {
|
if (!author) {
|
||||||
res = error(_("unable to parse commit author"));
|
res = error(_("unable to parse commit author"));
|
||||||
@ -1418,31 +1345,6 @@ static int try_to_commit(struct repository *r,
|
|||||||
commit_list_insert(current_head, &parents);
|
commit_list_insert(current_head, &parents);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->committer_date_is_author_date) {
|
|
||||||
int len = strlen(author);
|
|
||||||
struct ident_split ident;
|
|
||||||
struct strbuf date = STRBUF_INIT;
|
|
||||||
|
|
||||||
if (split_ident_line(&ident, author, len) < 0) {
|
|
||||||
res = error(_("malformed ident line"));
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (!ident.date_begin) {
|
|
||||||
res = error(_("corrupted author without date information"));
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
strbuf_addf(&date, "@%.*s %.*s",
|
|
||||||
(int)(ident.date_end - ident.date_begin), ident.date_begin,
|
|
||||||
(int)(ident.tz_end - ident.tz_begin), ident.tz_begin);
|
|
||||||
res = setenv("GIT_COMMITTER_DATE",
|
|
||||||
opts->ignore_date ? "" : date.buf, 1);
|
|
||||||
strbuf_release(&date);
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write_index_as_tree(&tree, r->index, r->index_file, 0, NULL)) {
|
if (write_index_as_tree(&tree, r->index, r->index_file, 0, NULL)) {
|
||||||
res = error(_("git write-tree failed to write a tree"));
|
res = error(_("git write-tree failed to write a tree"));
|
||||||
goto out;
|
goto out;
|
||||||
@ -1502,15 +1404,6 @@ static int try_to_commit(struct repository *r,
|
|||||||
|
|
||||||
reset_ident_date();
|
reset_ident_date();
|
||||||
|
|
||||||
if (opts->ignore_date) {
|
|
||||||
author = ignore_author_date(author);
|
|
||||||
if (!author) {
|
|
||||||
res = -1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
free(author_to_free);
|
|
||||||
author_to_free = (char *)author;
|
|
||||||
}
|
|
||||||
if (commit_tree_extended(msg->buf, msg->len, &tree, parents,
|
if (commit_tree_extended(msg->buf, msg->len, &tree, parents,
|
||||||
oid, author, opts->gpg_sign, extra)) {
|
oid, author, opts->gpg_sign, extra)) {
|
||||||
res = error(_("failed to write commit object"));
|
res = error(_("failed to write commit object"));
|
||||||
@ -1531,7 +1424,7 @@ out:
|
|||||||
free_commit_extra_headers(extra);
|
free_commit_extra_headers(extra);
|
||||||
strbuf_release(&err);
|
strbuf_release(&err);
|
||||||
strbuf_release(&commit_msg);
|
strbuf_release(&commit_msg);
|
||||||
free(author_to_free);
|
free(amend_author);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -2599,16 +2492,6 @@ static int read_populate_opts(struct replay_opts *opts)
|
|||||||
opts->signoff = 1;
|
opts->signoff = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file_exists(rebase_path_cdate_is_adate())) {
|
|
||||||
opts->allow_ff = 0;
|
|
||||||
opts->committer_date_is_author_date = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_exists(rebase_path_ignore_date())) {
|
|
||||||
opts->allow_ff = 0;
|
|
||||||
opts->ignore_date = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_exists(rebase_path_reschedule_failed_exec()))
|
if (file_exists(rebase_path_reschedule_failed_exec()))
|
||||||
opts->reschedule_failed_exec = 1;
|
opts->reschedule_failed_exec = 1;
|
||||||
|
|
||||||
@ -2691,10 +2574,6 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
|
|||||||
write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
|
write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
|
||||||
if (opts->signoff)
|
if (opts->signoff)
|
||||||
write_file(rebase_path_signoff(), "--signoff\n");
|
write_file(rebase_path_signoff(), "--signoff\n");
|
||||||
if (opts->committer_date_is_author_date)
|
|
||||||
write_file(rebase_path_cdate_is_adate(), "%s", "");
|
|
||||||
if (opts->ignore_date)
|
|
||||||
write_file(rebase_path_ignore_date(), "%s", "");
|
|
||||||
if (opts->reschedule_failed_exec)
|
if (opts->reschedule_failed_exec)
|
||||||
write_file(rebase_path_reschedule_failed_exec(), "%s", "");
|
write_file(rebase_path_reschedule_failed_exec(), "%s", "");
|
||||||
|
|
||||||
@ -3635,8 +3514,6 @@ static int do_merge(struct repository *r,
|
|||||||
argv_array_push(&cmd.args, git_path_merge_msg(r));
|
argv_array_push(&cmd.args, git_path_merge_msg(r));
|
||||||
if (opts->gpg_sign)
|
if (opts->gpg_sign)
|
||||||
argv_array_push(&cmd.args, opts->gpg_sign);
|
argv_array_push(&cmd.args, opts->gpg_sign);
|
||||||
if (opts->ignore_date)
|
|
||||||
push_dates(&cmd, opts->committer_date_is_author_date);
|
|
||||||
|
|
||||||
/* Add the tips to be merged */
|
/* Add the tips to be merged */
|
||||||
for (j = to_merge; j; j = j->next)
|
for (j = to_merge; j; j = j->next)
|
||||||
@ -3909,9 +3786,7 @@ static int pick_commits(struct repository *r,
|
|||||||
setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
|
setenv(GIT_REFLOG_ACTION, action_name(opts), 0);
|
||||||
if (opts->allow_ff)
|
if (opts->allow_ff)
|
||||||
assert(!(opts->signoff || opts->no_commit ||
|
assert(!(opts->signoff || opts->no_commit ||
|
||||||
opts->record_origin || opts->edit ||
|
opts->record_origin || opts->edit));
|
||||||
opts->committer_date_is_author_date ||
|
|
||||||
opts->ignore_date));
|
|
||||||
if (read_and_refresh_cache(r, opts))
|
if (read_and_refresh_cache(r, opts))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -43,8 +43,6 @@ struct replay_opts {
|
|||||||
int verbose;
|
int verbose;
|
||||||
int quiet;
|
int quiet;
|
||||||
int reschedule_failed_exec;
|
int reschedule_failed_exec;
|
||||||
int committer_date_is_author_date;
|
|
||||||
int ignore_date;
|
|
||||||
|
|
||||||
int mainline;
|
int mainline;
|
||||||
|
|
||||||
|
@ -246,6 +246,17 @@ test_expect_success 'cone mode: init and set' '
|
|||||||
test_cmp expect dir
|
test_cmp expect dir
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'cone mode: list' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
folder1
|
||||||
|
folder2
|
||||||
|
EOF
|
||||||
|
git -C repo sparse-checkout set --stdin <expect &&
|
||||||
|
git -C repo sparse-checkout list >actual 2>err &&
|
||||||
|
test_must_be_empty err &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'cone mode: set with nested folders' '
|
test_expect_success 'cone mode: set with nested folders' '
|
||||||
git -C repo sparse-checkout set deep deep/deeper1/deepest 2>err &&
|
git -C repo sparse-checkout set deep deep/deeper1/deepest 2>err &&
|
||||||
test_line_count = 0 err &&
|
test_line_count = 0 err &&
|
||||||
@ -329,4 +340,32 @@ test_expect_success 'cone mode: set with core.ignoreCase=true' '
|
|||||||
test_cmp expect dir
|
test_cmp expect dir
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'interaction with submodules' '
|
||||||
|
git clone repo super &&
|
||||||
|
(
|
||||||
|
cd super &&
|
||||||
|
mkdir modules &&
|
||||||
|
git submodule add ../repo modules/child &&
|
||||||
|
git add . &&
|
||||||
|
git commit -m "add submodule" &&
|
||||||
|
git sparse-checkout init --cone &&
|
||||||
|
git sparse-checkout set folder1
|
||||||
|
) &&
|
||||||
|
list_files super >dir &&
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
a
|
||||||
|
folder1
|
||||||
|
modules
|
||||||
|
EOF
|
||||||
|
test_cmp expect dir &&
|
||||||
|
list_files super/modules/child >dir &&
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
a
|
||||||
|
deep
|
||||||
|
folder1
|
||||||
|
folder2
|
||||||
|
EOF
|
||||||
|
test_cmp expect dir
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -61,6 +61,8 @@ test_rebase_am_only () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test_rebase_am_only --whitespace=fix
|
test_rebase_am_only --whitespace=fix
|
||||||
|
test_rebase_am_only --ignore-whitespace
|
||||||
|
test_rebase_am_only --committer-date-is-author-date
|
||||||
test_rebase_am_only -C4
|
test_rebase_am_only -C4
|
||||||
|
|
||||||
test_expect_success REBASE_P '--preserve-merges incompatible with --signoff' '
|
test_expect_success REBASE_P '--preserve-merges incompatible with --signoff' '
|
||||||
|
@ -1,131 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# Copyright (c) 2019 Rohit Ashiwal
|
|
||||||
#
|
|
||||||
|
|
||||||
test_description='tests to ensure compatibility between am and interactive backends'
|
|
||||||
|
|
||||||
. ./test-lib.sh
|
|
||||||
|
|
||||||
GIT_AUTHOR_DATE="1999-04-02T08:03:20+05:30"
|
|
||||||
export GIT_AUTHOR_DATE
|
|
||||||
|
|
||||||
# This is a special case in which both am and interactive backends
|
|
||||||
# provide the same output. It was done intentionally because
|
|
||||||
# both the backends fall short of optimal behaviour.
|
|
||||||
test_expect_success 'setup' '
|
|
||||||
git checkout -b topic &&
|
|
||||||
q_to_tab >file <<-\EOF &&
|
|
||||||
line 1
|
|
||||||
Qline 2
|
|
||||||
line 3
|
|
||||||
EOF
|
|
||||||
git add file &&
|
|
||||||
git commit -m "add file" &&
|
|
||||||
cat >file <<-\EOF &&
|
|
||||||
line 1
|
|
||||||
new line 2
|
|
||||||
line 3
|
|
||||||
EOF
|
|
||||||
git commit -am "update file" &&
|
|
||||||
git tag side &&
|
|
||||||
test_commit commit1 foo foo1 &&
|
|
||||||
test_commit commit2 foo foo2 &&
|
|
||||||
test_commit commit3 foo foo3 &&
|
|
||||||
|
|
||||||
git checkout --orphan master &&
|
|
||||||
git rm --cached foo &&
|
|
||||||
rm foo &&
|
|
||||||
sed -e "s/^|//" >file <<-\EOF &&
|
|
||||||
|line 1
|
|
||||||
| line 2
|
|
||||||
|line 3
|
|
||||||
EOF
|
|
||||||
git add file &&
|
|
||||||
git commit -m "add file" &&
|
|
||||||
git tag main
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success '--ignore-whitespace works with am backend' '
|
|
||||||
cat >expect <<-\EOF &&
|
|
||||||
line 1
|
|
||||||
new line 2
|
|
||||||
line 3
|
|
||||||
EOF
|
|
||||||
test_must_fail git rebase main side &&
|
|
||||||
git rebase --abort &&
|
|
||||||
git rebase --ignore-whitespace main side &&
|
|
||||||
test_cmp expect file
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success '--ignore-whitespace works with interactive backend' '
|
|
||||||
cat >expect <<-\EOF &&
|
|
||||||
line 1
|
|
||||||
new line 2
|
|
||||||
line 3
|
|
||||||
EOF
|
|
||||||
test_must_fail git rebase --merge main side &&
|
|
||||||
git rebase --abort &&
|
|
||||||
git rebase --merge --ignore-whitespace main side &&
|
|
||||||
test_cmp expect file
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success '--committer-date-is-author-date works with am backend' '
|
|
||||||
git commit --amend &&
|
|
||||||
git rebase --committer-date-is-author-date HEAD^ &&
|
|
||||||
git show HEAD --pretty="format:%ai" >authortime &&
|
|
||||||
git show HEAD --pretty="format:%ci" >committertime &&
|
|
||||||
test_cmp authortime committertime
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success '--committer-date-is-author-date works with interactive backend' '
|
|
||||||
git commit --amend &&
|
|
||||||
git rebase -i --committer-date-is-author-date HEAD^ &&
|
|
||||||
git show HEAD --pretty="format:%ai" >authortime &&
|
|
||||||
git show HEAD --pretty="format:%ci" >committertime &&
|
|
||||||
test_cmp authortime committertime
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success '--committer-date-is-author-date works with rebase -r' '
|
|
||||||
git checkout side &&
|
|
||||||
git merge --no-ff commit3 &&
|
|
||||||
git rebase -r --root --committer-date-is-author-date &&
|
|
||||||
git rev-list HEAD >rev_list &&
|
|
||||||
while read HASH
|
|
||||||
do
|
|
||||||
git show $HASH --pretty="format:%ai" >authortime
|
|
||||||
git show $HASH --pretty="format:%ci" >committertime
|
|
||||||
test_cmp authortime committertime
|
|
||||||
done <rev_list
|
|
||||||
'
|
|
||||||
|
|
||||||
# Checking for +0000 in author time is enough since default
|
|
||||||
# timezone is UTC, but the timezone used while committing
|
|
||||||
# sets to +0530.
|
|
||||||
test_expect_success '--ignore-date works with am backend' '
|
|
||||||
git commit --amend --date="$GIT_AUTHOR_DATE" &&
|
|
||||||
git rebase --ignore-date HEAD^ &&
|
|
||||||
git show HEAD --pretty="format:%ai" >authortime &&
|
|
||||||
grep "+0000" authortime
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success '--ignore-date works with interactive backend' '
|
|
||||||
git commit --amend --date="$GIT_AUTHOR_DATE" &&
|
|
||||||
git rebase --ignore-date -i HEAD^ &&
|
|
||||||
git show HEAD --pretty="format:%ai" >authortime &&
|
|
||||||
grep "+0000" authortime
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success '--ignore-date works with rebase -r' '
|
|
||||||
git checkout side &&
|
|
||||||
git merge --no-ff commit3 &&
|
|
||||||
git rebase -r --root --ignore-date &&
|
|
||||||
git rev-list HEAD >rev_list &&
|
|
||||||
while read HASH
|
|
||||||
do
|
|
||||||
git show $HASH --pretty="format:%ai" >authortime
|
|
||||||
grep "+0000" authortime
|
|
||||||
done <rev_list
|
|
||||||
'
|
|
||||||
|
|
||||||
test_done
|
|
@ -240,4 +240,75 @@ test_expect_success 'log --graph with octopus merge with column joining its penu
|
|||||||
EOF
|
EOF
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'log --graph with multiple tips' '
|
||||||
|
git checkout --orphan 6_1 &&
|
||||||
|
test_commit 6_A &&
|
||||||
|
git branch 6_2 &&
|
||||||
|
git branch 6_4 &&
|
||||||
|
test_commit 6_B &&
|
||||||
|
git branch 6_3 &&
|
||||||
|
test_commit 6_C &&
|
||||||
|
git checkout 6_2 && test_commit 6_D &&
|
||||||
|
git checkout 6_3 && test_commit 6_E &&
|
||||||
|
git checkout -b 6_5 6_1 &&
|
||||||
|
git merge --no-ff 6_2 -m 6_F &&
|
||||||
|
git checkout 6_4 && test_commit 6_G &&
|
||||||
|
git checkout 6_3 &&
|
||||||
|
git merge --no-ff 6_4 -m 6_H &&
|
||||||
|
git checkout 6_1 &&
|
||||||
|
git merge --no-ff 6_2 -m 6_I &&
|
||||||
|
|
||||||
|
check_graph 6_1 6_3 6_5 <<-\EOF
|
||||||
|
* 6_I
|
||||||
|
|\
|
||||||
|
| | * 6_H
|
||||||
|
| | |\
|
||||||
|
| | | * 6_G
|
||||||
|
| | * | 6_E
|
||||||
|
| | | | * 6_F
|
||||||
|
| |_|_|/|
|
||||||
|
|/| | |/
|
||||||
|
| | |/|
|
||||||
|
| |/| |
|
||||||
|
| * | | 6_D
|
||||||
|
| | |/
|
||||||
|
| |/|
|
||||||
|
* | | 6_C
|
||||||
|
| |/
|
||||||
|
|/|
|
||||||
|
* | 6_B
|
||||||
|
|/
|
||||||
|
* 6_A
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'log --graph with multiple tips and colors' '
|
||||||
|
test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
|
||||||
|
cat >expect.colors <<-\EOF &&
|
||||||
|
* 6_I
|
||||||
|
<RED>|<RESET><GREEN>\<RESET>
|
||||||
|
<RED>|<RESET> <GREEN>|<RESET> * 6_H
|
||||||
|
<RED>|<RESET> <GREEN>|<RESET> <YELLOW>|<RESET><BLUE>\<RESET>
|
||||||
|
<RED>|<RESET> <GREEN>|<RESET> <YELLOW>|<RESET> * 6_G
|
||||||
|
<RED>|<RESET> <GREEN>|<RESET> <YELLOW>|<RESET> <BLUE>|<RESET> * 6_F
|
||||||
|
<RED>|<RESET> <GREEN>|<RESET><RED>_<RESET><YELLOW>|<RESET><RED>_<RESET><BLUE>|<RESET><RED>/<RESET><GREEN>|<RESET>
|
||||||
|
<RED>|<RESET><RED>/<RESET><GREEN>|<RESET> <YELLOW>|<RESET> <BLUE>|<RESET><GREEN>/<RESET>
|
||||||
|
<RED>|<RESET> <GREEN>|<RESET> <YELLOW>|<RESET><GREEN>/<RESET><BLUE>|<RESET>
|
||||||
|
<RED>|<RESET> <GREEN>|<RESET><GREEN>/<RESET><YELLOW>|<RESET> <BLUE>|<RESET>
|
||||||
|
<RED>|<RESET> <GREEN>|<RESET> * <BLUE>|<RESET> 6_E
|
||||||
|
<RED>|<RESET> * <CYAN>|<RESET> <BLUE>|<RESET> 6_D
|
||||||
|
<RED>|<RESET> <BLUE>|<RESET> <CYAN>|<RESET><BLUE>/<RESET>
|
||||||
|
<RED>|<RESET> <BLUE>|<RESET><BLUE>/<RESET><CYAN>|<RESET>
|
||||||
|
* <BLUE>|<RESET> <CYAN>|<RESET> 6_C
|
||||||
|
<CYAN>|<RESET> <BLUE>|<RESET><CYAN>/<RESET>
|
||||||
|
<CYAN>|<RESET><CYAN>/<RESET><BLUE>|<RESET>
|
||||||
|
* <BLUE>|<RESET> 6_B
|
||||||
|
<BLUE>|<RESET><BLUE>/<RESET>
|
||||||
|
* 6_A
|
||||||
|
EOF
|
||||||
|
git log --color=always --graph --date-order --pretty=tformat:%s 6_1 6_3 6_5 >actual.colors.raw &&
|
||||||
|
test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
|
||||||
|
test_cmp expect.colors actual.colors
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -207,6 +207,9 @@ test_expect_success MINGW 'prevent git~1 squatting on Windows' '
|
|||||||
git hash-object -w --stdin)" &&
|
git hash-object -w --stdin)" &&
|
||||||
rev="$(git rev-parse --verify HEAD)" &&
|
rev="$(git rev-parse --verify HEAD)" &&
|
||||||
hash="$(echo x | git hash-object -w --stdin)" &&
|
hash="$(echo x | git hash-object -w --stdin)" &&
|
||||||
|
test_must_fail git update-index --add \
|
||||||
|
--cacheinfo 160000,$rev,d\\a 2>err &&
|
||||||
|
test_i18ngrep "Invalid path" err &&
|
||||||
git -c core.protectNTFS=false update-index --add \
|
git -c core.protectNTFS=false update-index --add \
|
||||||
--cacheinfo 100644,$modules,.gitmodules \
|
--cacheinfo 100644,$modules,.gitmodules \
|
||||||
--cacheinfo 160000,$rev,c \
|
--cacheinfo 160000,$rev,c \
|
||||||
@ -214,9 +217,7 @@ test_expect_success MINGW 'prevent git~1 squatting on Windows' '
|
|||||||
--cacheinfo 100644,$hash,d./a/x \
|
--cacheinfo 100644,$hash,d./a/x \
|
||||||
--cacheinfo 100644,$hash,d./a/..git &&
|
--cacheinfo 100644,$hash,d./a/..git &&
|
||||||
test_tick &&
|
test_tick &&
|
||||||
git -c core.protectNTFS=false commit -m "module" &&
|
git -c core.protectNTFS=false commit -m "module"
|
||||||
test_must_fail git show HEAD: 2>err &&
|
|
||||||
test_i18ngrep backslash err
|
|
||||||
) &&
|
) &&
|
||||||
test_must_fail git -c core.protectNTFS=false \
|
test_must_fail git -c core.protectNTFS=false \
|
||||||
clone --recurse-submodules squatting squatting-clone 2>err &&
|
clone --recurse-submodules squatting squatting-clone 2>err &&
|
||||||
|
@ -43,12 +43,6 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
|
|||||||
strbuf_addstr(err, _("empty filename in tree entry"));
|
strbuf_addstr(err, _("empty filename in tree entry"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#ifdef GIT_WINDOWS_NATIVE
|
|
||||||
if (protect_ntfs && strchr(path, '\\')) {
|
|
||||||
strbuf_addf(err, _("filename in tree entry contains backslash: '%s'"), path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
len = strlen(path) + 1;
|
len = strlen(path) + 1;
|
||||||
|
|
||||||
/* Initialize the descriptor entry */
|
/* Initialize the descriptor entry */
|
||||||
|
Reference in New Issue
Block a user