mirror of
https://github.com/git/git.git
synced 2026-03-13 10:23:30 +01:00
Merge commit 'b8de7f764e1a9f6e8dfb587a6145906394fa607d'
This commit is contained in:
@@ -131,7 +131,7 @@ clean:
|
||||
user-manual.xml: user-manual.txt user-manual.conf
|
||||
$(ASCIIDOC) -b docbook -d book $<
|
||||
|
||||
XSLT = http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl
|
||||
XSLT = docbook.xsl
|
||||
XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
|
||||
|
||||
user-manual.html: user-manual.xml
|
||||
|
||||
@@ -32,8 +32,9 @@ Updates since v1.5.2
|
||||
- "git rebase" learned an "interactive" mode that let you
|
||||
pick and reorder which commits to rebuild.
|
||||
|
||||
- "git fsck" can save its findings in $GIT_DIR/lost-found,
|
||||
without a separate invocation of "git lost-found" command.
|
||||
- "git fsck" can save its findings in $GIT_DIR/lost-found, without a
|
||||
separate invocation of "git lost-found" command. The blobs stored by
|
||||
lost-found are stored in plain format to allow you to grep in them.
|
||||
|
||||
- $GIT_WORK_TREE environment variable can be used together with
|
||||
$GIT_DIR to work in a subdirectory of a working tree that is
|
||||
@@ -49,6 +50,10 @@ Updates since v1.5.2
|
||||
- "git-cvsserver" learned new options (--base-path, --export-all,
|
||||
--strict-paths) inspired by git-daemon.
|
||||
|
||||
- "git-commit" can use "-t templatefile" option and commit.template
|
||||
configuration variable to prime the commit message given to you in the
|
||||
editor.
|
||||
|
||||
- "git-submodule" command helps you manage the projects from
|
||||
the superproject that contain them.
|
||||
|
||||
@@ -105,9 +110,30 @@ Updates since v1.5.2
|
||||
|
||||
* Updated behavior of existing commands.
|
||||
|
||||
- "gitweb" can offer multiple snapshot formats.
|
||||
|
||||
***NOTE*** Unfortunately, this changes the format of the
|
||||
$feature{snapshot}{default} entry in the per-site
|
||||
configuration file 'gitweb_config.perl'. It used to be a
|
||||
three-element tuple that describe a single format; with the
|
||||
new configuration item format, you only have to say the name
|
||||
of the format ('tgz', 'tbz2' or 'zip'). Please update the
|
||||
your configuration file accordingly.
|
||||
|
||||
- The editor to use with many interactive commands can be
|
||||
overridden with GIT_EDITOR environment variable, or if it
|
||||
does not exist, with core.editor configuration variable. As
|
||||
before, if you have neither, environment variables VISUAL
|
||||
and EDITOR are consulted in this order, and then finally we
|
||||
fall back on "vi".
|
||||
|
||||
- "git rm --cached" does not complain when removing a newly
|
||||
added file from the index anymore.
|
||||
|
||||
- Options to "git log" to affect how --grep/--author options look for
|
||||
given strings now have shorter abbreviations. -i is for ignore case,
|
||||
and -E is for extended regexp.
|
||||
|
||||
- "git svn dcommit" retains local merge information.
|
||||
|
||||
- "git config" to set values also honors type flags like --bool
|
||||
@@ -189,6 +215,10 @@ Updates since v1.5.2
|
||||
git-fast-import (also in contrib). The man page and p4
|
||||
rpm have been removed as well.
|
||||
|
||||
- "git mailinfo" (hence "am") now tries to see if the message
|
||||
is in utf-8 first, instead of assuming iso-8859-1, if
|
||||
incoming e-mail does not say what encoding it is in.
|
||||
|
||||
* Builds
|
||||
|
||||
- old-style function definitions (most notably, a function
|
||||
@@ -232,6 +262,6 @@ this release, unless otherwise noted.
|
||||
|
||||
--
|
||||
exec >/var/tmp/1
|
||||
O=v1.5.3-rc2
|
||||
O=v1.5.3-rc3
|
||||
echo O=`git describe refs/heads/master`
|
||||
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
|
||||
|
||||
@@ -27,7 +27,13 @@ ifdef::backend-docbook[]
|
||||
[listingblock]
|
||||
<example><title>{title}</title>
|
||||
<literallayout>
|
||||
ifdef::doctype-manpage[]
|
||||
.ft C
|
||||
endif::doctype-manpage[]
|
||||
|
|
||||
ifdef::doctype-manpage[]
|
||||
.ft
|
||||
endif::doctype-manpage[]
|
||||
</literallayout>
|
||||
{title#}</example>
|
||||
endif::backend-docbook[]
|
||||
|
||||
@@ -281,6 +281,14 @@ core.excludesfile::
|
||||
of files which are not meant to be tracked. See
|
||||
gitlink:gitignore[5].
|
||||
|
||||
core.editor::
|
||||
Commands such as `commit` and `tag` that lets you edit
|
||||
messages by lauching an editor uses the value of this
|
||||
variable when it is set, and the environment variable
|
||||
`GIT_EDITOR` is not set. The order of preference is
|
||||
`GIT_EDITOR` environment, `core.editor`, `VISUAL` and
|
||||
`EDITOR` environment variables and then finally `vi`.
|
||||
|
||||
core.pager::
|
||||
The command that git will use to paginate output. Can be overridden
|
||||
with the `GIT_PAGER` environment variable.
|
||||
@@ -385,6 +393,9 @@ color.status.<slot>::
|
||||
or `untracked` (files which are not tracked by git). The values of
|
||||
these variables may be specified as in color.branch.<slot>.
|
||||
|
||||
commit.template::
|
||||
Specify a file to use as the template for new commit messages.
|
||||
|
||||
diff.renameLimit::
|
||||
The number of files to consider when performing the copy/rename
|
||||
detection; equivalent to the git diff option '-l'.
|
||||
|
||||
@@ -4,6 +4,13 @@
|
||||
-u::
|
||||
Synonym for "-p".
|
||||
|
||||
-U<n>::
|
||||
Shorthand for "--unified=<n>".
|
||||
|
||||
--unified=<n>::
|
||||
Generate diffs with <n> lines of context instead of
|
||||
the usual three. Implies "-p".
|
||||
|
||||
--raw::
|
||||
Generate the raw format.
|
||||
|
||||
@@ -36,7 +43,9 @@
|
||||
Synonym for "-p --stat".
|
||||
|
||||
-z::
|
||||
\0 line termination on output
|
||||
NUL-line termination on output. This affects the --raw
|
||||
output field terminator. Also output from commands such
|
||||
as "git-log" will be delimited with NUL between commits.
|
||||
|
||||
--name-only::
|
||||
Show only names of changed files.
|
||||
|
||||
5
Documentation/docbook.xsl
Normal file
5
Documentation/docbook.xsl
Normal file
@@ -0,0 +1,5 @@
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||
version='1.0'>
|
||||
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/>
|
||||
<xsl:output method="html" encoding="UTF-8" indent="no" />
|
||||
</xsl:stylesheet>
|
||||
@@ -130,8 +130,8 @@ Delete unneeded branch::
|
||||
------------
|
||||
$ git clone git://git.kernel.org/.../git.git my.git
|
||||
$ cd my.git
|
||||
$ git branch -d -r todo html man <1>
|
||||
$ git branch -D test <2>
|
||||
$ git branch -d -r origin/todo origin/html origin/man <1>
|
||||
$ git branch -D test <2>
|
||||
------------
|
||||
+
|
||||
<1> delete remote-tracking branches "todo", "html", "man"
|
||||
|
||||
@@ -74,6 +74,13 @@ OPTIONS
|
||||
-m <msg>|--message=<msg>::
|
||||
Use the given <msg> as the commit message.
|
||||
|
||||
-t <file>|--template=<file>::
|
||||
Use the contents of the given file as the initial version
|
||||
of the commit message. The editor is invoked and you can
|
||||
make subsequent changes. If a message is specified using
|
||||
the `-m` or `-F` options, this option has no effect. This
|
||||
overrides the `commit.template` configuration variable.
|
||||
|
||||
-s|--signoff::
|
||||
Add Signed-off-by line at the end of the commit message.
|
||||
|
||||
@@ -244,10 +251,12 @@ on the Subject: line and the rest of the commit in the body.
|
||||
|
||||
include::i18n.txt[]
|
||||
|
||||
ENVIRONMENT VARIABLES
|
||||
---------------------
|
||||
The command specified by either the VISUAL or EDITOR environment
|
||||
variables is used to edit the commit log message.
|
||||
ENVIRONMENT AND CONFIGURATION VARIABLES
|
||||
---------------------------------------
|
||||
The editor used to edit the commit log message will be chosen from the
|
||||
GIT_EDITOR environment variable, the core.editor configuration variable, the
|
||||
VISUAL environment variable, or the EDITOR environment variable (in that
|
||||
order).
|
||||
|
||||
HOOKS
|
||||
-----
|
||||
|
||||
@@ -102,17 +102,14 @@ Limiting the diff output::
|
||||
+
|
||||
------------
|
||||
$ git diff --diff-filter=MRC <1>
|
||||
$ git diff --name-status -r <2>
|
||||
$ git diff --name-status <2>
|
||||
$ git diff arch/i386 include/asm-i386 <3>
|
||||
------------
|
||||
+
|
||||
<1> show only modification, rename and copy, but not addition
|
||||
nor deletion.
|
||||
<2> show only names and the nature of change, but not actual
|
||||
diff output. --name-status disables usual patch generation
|
||||
which in turn also disables recursive behavior, so without -r
|
||||
you would only see the directory name if there is a change in a
|
||||
file in a subdirectory.
|
||||
diff output.
|
||||
<3> limit diff output to named subtrees.
|
||||
|
||||
Munging the diff output::
|
||||
|
||||
@@ -12,7 +12,7 @@ SYNOPSIS
|
||||
[--index-filter <command>] [--parent-filter <command>]
|
||||
[--msg-filter <command>] [--commit-filter <command>]
|
||||
[--tag-name-filter <command>] [--subdirectory-filter <directory>]
|
||||
[-d <directory>] <new-branch-name> [<rev-list options>...]
|
||||
[-d <directory>] [-f | --force] [<rev-list options>...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -26,10 +26,9 @@ information) will be preserved.
|
||||
The command takes the new branch name as a mandatory argument and
|
||||
the filters as optional arguments. If you specify no filters, the
|
||||
commits will be recommitted without any changes, which would normally
|
||||
have no effect and result in the new branch pointing to the same
|
||||
branch as your current branch. Nevertheless, this may be useful in
|
||||
the future for compensating for some git bugs or such, therefore
|
||||
such a usage is permitted.
|
||||
have no effect. Nevertheless, this may be useful in the future for
|
||||
compensating for some git bugs or such, therefore such a usage is
|
||||
permitted.
|
||||
|
||||
*WARNING*! The rewritten history will have different object names for all
|
||||
the objects and will not converge with the original branch. You will not
|
||||
@@ -38,8 +37,9 @@ original branch. Please do not use this command if you do not know the
|
||||
full implications, and avoid using it anyway, if a simple single commit
|
||||
would suffice to fix your problem.
|
||||
|
||||
Always verify that the rewritten version is correct before disposing
|
||||
the original branch.
|
||||
Always verify that the rewritten version is correct: The original refs,
|
||||
if different from the rewritten ones, will be stored in the namespace
|
||||
'refs/original/'.
|
||||
|
||||
Note that since this operation is extensively I/O expensive, it might
|
||||
be a good idea to redirect the temporary directory off-disk, e.g. on
|
||||
@@ -142,6 +142,11 @@ definition impossible to preserve signatures at any rate.)
|
||||
does this in the '.git-rewrite/' directory but you can override
|
||||
that choice by this parameter.
|
||||
|
||||
-f\|--force::
|
||||
`git filter-branch` refuses to start with an existing temporary
|
||||
directory or when there are already refs starting with
|
||||
'refs/original/', unless forced.
|
||||
|
||||
<rev-list-options>::
|
||||
When options are given after the new branch name, they will
|
||||
be passed to gitlink:git-rev-list[1]. Only commits in the resulting
|
||||
@@ -156,14 +161,14 @@ Suppose you want to remove a file (containing confidential information
|
||||
or copyright violation) from all commits:
|
||||
|
||||
-------------------------------------------------------
|
||||
git filter-branch --tree-filter 'rm filename' newbranch
|
||||
git filter-branch --tree-filter 'rm filename' HEAD
|
||||
-------------------------------------------------------
|
||||
|
||||
A significantly faster version:
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
git filter-branch --index-filter 'git update-index --remove filename' newbranch
|
||||
-------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------
|
||||
git filter-branch --index-filter 'git update-index --remove filename' HEAD
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
Now, you will get the rewritten history saved in the branch 'newbranch'
|
||||
(your current branch is left untouched).
|
||||
@@ -172,25 +177,25 @@ To set a commit (which typically is at the tip of another
|
||||
history) to be the parent of the current initial commit, in
|
||||
order to paste the other history behind the current history:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
git filter-branch --parent-filter 'sed "s/^\$/-p <graft-id>/"' newbranch
|
||||
------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------
|
||||
git filter-branch --parent-filter 'sed "s/^\$/-p <graft-id>/"' HEAD
|
||||
-------------------------------------------------------------------
|
||||
|
||||
(if the parent string is empty - therefore we are dealing with the
|
||||
initial commit - add graftcommit as a parent). Note that this assumes
|
||||
history with a single root (that is, no merge without common ancestors
|
||||
happened). If this is not the case, use:
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------
|
||||
git filter-branch --parent-filter \
|
||||
'cat; test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>"' newbranch
|
||||
-------------------------------------------------------------------------------
|
||||
'cat; test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>"' HEAD
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
or even simpler:
|
||||
|
||||
-----------------------------------------------
|
||||
echo "$commit-id $graft-id" >> .git/info/grafts
|
||||
git filter-branch newbranch $graft-id..
|
||||
git filter-branch $graft-id..HEAD
|
||||
-----------------------------------------------
|
||||
|
||||
To remove commits authored by "Darl McBribe" from the history:
|
||||
@@ -208,7 +213,7 @@ git filter-branch --commit-filter '
|
||||
done;
|
||||
else
|
||||
git commit-tree "$@";
|
||||
fi' newbranch
|
||||
fi' HEAD
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
The shift magic first throws away the tree id and then the -p
|
||||
@@ -238,14 +243,14 @@ A--B-----C
|
||||
To rewrite only commits D,E,F,G,H, but leave A, B and C alone, use:
|
||||
|
||||
--------------------------------
|
||||
git filter-branch ... new-H C..H
|
||||
git filter-branch ... C..H
|
||||
--------------------------------
|
||||
|
||||
To rewrite commits E,F,G,H, use one of these:
|
||||
|
||||
----------------------------------------
|
||||
git filter-branch ... new-H C..H --not D
|
||||
git filter-branch ... new-H D..H --not C
|
||||
git filter-branch ... C..H --not D
|
||||
git filter-branch ... D..H --not C
|
||||
----------------------------------------
|
||||
|
||||
To move the whole tree into a subdirectory, or remove it from there:
|
||||
@@ -255,7 +260,7 @@ git filter-branch --index-filter \
|
||||
'git ls-files -s | sed "s-\t-&newsubdir/-" |
|
||||
GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
|
||||
git update-index --index-info &&
|
||||
mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' directorymoved
|
||||
mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' HEAD
|
||||
---------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
@@ -65,8 +65,10 @@ index file and all SHA1 references in .git/refs/* as heads.
|
||||
Be chatty.
|
||||
|
||||
--lost-found::
|
||||
Write dangling refs into .git/lost-found/commit/ or
|
||||
.git/lost-found/other/, depending on type.
|
||||
Write dangling objects into .git/lost-found/commit/ or
|
||||
.git/lost-found/other/, depending on type. If the object is
|
||||
a blob, the contents are written into the file, rather than
|
||||
its object name.
|
||||
|
||||
It tests SHA1 and general object sanity, and it does full tracking of
|
||||
the resulting reachability and everything else. It prints out any
|
||||
|
||||
@@ -27,7 +27,8 @@ SYNOPSIS
|
||||
[ \--cherry-pick ]
|
||||
[ \--encoding[=<encoding>] ]
|
||||
[ \--(author|committer|grep)=<pattern> ]
|
||||
[ \--regexp-ignore-case ] [ \--extended-regexp ]
|
||||
[ \--regexp-ignore-case | \-i ]
|
||||
[ \--extended-regexp | \-E ]
|
||||
[ \--date={local|relative|default|iso|rfc|short} ]
|
||||
[ [\--objects | \--objects-edge] [ \--unpacked ] ]
|
||||
[ \--pretty | \--header ]
|
||||
@@ -36,6 +37,7 @@ SYNOPSIS
|
||||
[ \--merge ]
|
||||
[ \--reverse ]
|
||||
[ \--walk-reflogs ]
|
||||
[ \--no-walk ] [ \--do-walk ]
|
||||
<commit>... [ \-- <paths>... ]
|
||||
|
||||
DESCRIPTION
|
||||
@@ -227,11 +229,11 @@ limiting may be applied.
|
||||
Limit the commits output to ones with log message that
|
||||
matches the specified pattern (regular expression).
|
||||
|
||||
--regexp-ignore-case::
|
||||
-i, --regexp-ignore-case::
|
||||
|
||||
Match the regexp limiting patterns without regard to letters case.
|
||||
|
||||
--extended-regexp::
|
||||
-E, --extended-regexp::
|
||||
|
||||
Consider the limiting patterns to be extended regular expressions
|
||||
instead of the default basic regular expressions.
|
||||
@@ -397,6 +399,14 @@ These options are mostly targeted for packing of git repositories.
|
||||
Only useful with '--objects'; print the object IDs that are not
|
||||
in packs.
|
||||
|
||||
--no-walk::
|
||||
|
||||
Only show the given revs, but do not traverse their ancestors.
|
||||
|
||||
--do-walk::
|
||||
|
||||
Overrides a previous --no-walk.
|
||||
|
||||
|
||||
include::pretty-formats.txt[]
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@ The --cc option must be repeated for each user you want on the cc list.
|
||||
value; if that is unspecified, default to --chain-reply-to.
|
||||
|
||||
--compose::
|
||||
Use $EDITOR to edit an introductory message for the
|
||||
patch series.
|
||||
Use $GIT_EDITOR, core.editor, $VISUAL, or $EDITOR to edit an
|
||||
introductory message for the patch series.
|
||||
|
||||
--from::
|
||||
Specify the sender of the emails. This will default to
|
||||
|
||||
@@ -8,7 +8,8 @@ git-stash - Stash the changes in a dirty working directory away
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-stash' (save | list | show [<stash>] | apply [<stash>] | clear)
|
||||
'git-stash' (list | show [<stash>] | apply [<stash>] | clear)
|
||||
'git-stash' [save] [message...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -22,7 +23,9 @@ The modifications stashed away by this command can be listed with
|
||||
`git-stash list`, inspected with `git-stash show`, and restored
|
||||
(potentially on top of a different commit) with `git-stash apply`.
|
||||
Calling git-stash without any arguments is equivalent to `git-stash
|
||||
save`.
|
||||
save`. A stash is by default listed as "WIP on 'branchname' ...", but
|
||||
you can give a more descriptive message on the command line when
|
||||
you create one.
|
||||
|
||||
The latest stash you created is stored in `$GIT_DIR/refs/stash`; older
|
||||
stashes are found in the reflog of this reference and can be named using
|
||||
@@ -48,8 +51,8 @@ list::
|
||||
based on.
|
||||
+
|
||||
----------------------------------------------------------------
|
||||
stash@{0}: submit: 6ebd0e2... Add git-stash
|
||||
stash@{1}: master: 9cc0589... Merge branch 'master' of gfi
|
||||
stash@{0}: WIP on submit: 6ebd0e2... Update git-stash documentation
|
||||
stash@{1}: On master: 9cc0589... Add git-stash
|
||||
----------------------------------------------------------------
|
||||
|
||||
show [<stash>]::
|
||||
|
||||
@@ -417,7 +417,9 @@ other
|
||||
See gitlink:git-merge[1]
|
||||
|
||||
'GIT_PAGER'::
|
||||
This environment variable overrides `$PAGER`.
|
||||
This environment variable overrides `$PAGER`. If it is set
|
||||
to an empty string or to the value "cat", git will not launch
|
||||
a pager.
|
||||
|
||||
'GIT_FLUSH'::
|
||||
If this environment variable is set to "1", then commands such
|
||||
|
||||
@@ -18,21 +18,26 @@ pattern.
|
||||
|
||||
When deciding whether to ignore a path, git normally checks
|
||||
`gitignore` patterns from multiple sources, with the following
|
||||
order of precedence:
|
||||
order of precedence, from highest to lowest (within one level of
|
||||
precedence, the last matching pattern decides the outcome):
|
||||
|
||||
* Patterns read from the file specified by the configuration
|
||||
variable 'core.excludesfile'.
|
||||
|
||||
* Patterns read from `$GIT_DIR/info/exclude`.
|
||||
* Patterns read from the command line for those commands that support
|
||||
them.
|
||||
|
||||
* Patterns read from a `.gitignore` file in the same directory
|
||||
as the path, or in any parent directory, ordered from the
|
||||
deepest such file to a file in the root of the repository.
|
||||
as the path, or in any parent directory, with patterns in the
|
||||
higher level files (up to the root) being overriden by those in
|
||||
lower level files down to the directory containing the file.
|
||||
These patterns match relative to the location of the
|
||||
`.gitignore` file. A project normally includes such
|
||||
`.gitignore` files in its repository, containing patterns for
|
||||
files generated as part of the project build.
|
||||
|
||||
* Patterns read from `$GIT_DIR/info/exclude`.
|
||||
|
||||
* Patterns read from the file specified by the configuration
|
||||
variable 'core.excludesfile'.
|
||||
|
||||
The underlying git plumbing tools, such as
|
||||
gitlink:git-ls-files[1] and gitlink:git-read-tree[1], read
|
||||
`gitignore` patterns specified by command-line options, or from
|
||||
@@ -49,7 +54,8 @@ Patterns have the following format:
|
||||
|
||||
- An optional prefix '!' which negates the pattern; any
|
||||
matching file excluded by a previous pattern will become
|
||||
included again.
|
||||
included again. If a negated pattern matches, this will
|
||||
override lower precedence patterns sources.
|
||||
|
||||
- If the pattern does not contain a slash '/', git treats it as
|
||||
a shell glob pattern and checks for a match against the
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
--pretty[='<format>']::
|
||||
|
||||
Pretty print the contents of the commit logs in a given format,
|
||||
Pretty-print the contents of the commit logs in a given format,
|
||||
where '<format>' can be one of 'oneline', 'short', 'medium',
|
||||
'full', 'fuller', 'email', 'raw' and 'format:<string>'.
|
||||
When left out the format default to 'medium'.
|
||||
When omitted, the format defaults to 'medium'.
|
||||
|
||||
--abbrev-commit::
|
||||
Instead of showing the full 40-byte hexadecimal commit object
|
||||
|
||||
@@ -449,7 +449,7 @@ Exploring git history
|
||||
|
||||
Git is best thought of as a tool for storing the history of a
|
||||
collection of files. It does this by storing compressed snapshots of
|
||||
the contents of a file heirarchy, together with "commits" which show
|
||||
the contents of a file hierarchy, together with "commits" which show
|
||||
the relationships between these snapshots.
|
||||
|
||||
Git provides extremely flexible and fast tools for exploring the
|
||||
@@ -1070,7 +1070,7 @@ about to commit:
|
||||
|
||||
-------------------------------------------------
|
||||
$ git diff --cached # difference between HEAD and the index; what
|
||||
# would be commited if you ran "commit" now.
|
||||
# would be committed if you ran "commit" now.
|
||||
$ git diff # difference between the index file and your
|
||||
# working directory; changes that would not
|
||||
# be included if you ran "commit" now.
|
||||
@@ -1257,7 +1257,7 @@ index 802992c,2b60207..0000000
|
||||
++>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
|
||||
-------------------------------------------------
|
||||
|
||||
Recall that the commit which will be commited after we resolve this
|
||||
Recall that the commit which will be committed after we resolve this
|
||||
conflict will have two parents instead of the usual one: one parent
|
||||
will be HEAD, the tip of the current branch; the other will be the
|
||||
tip of the other branch, which is stored temporarily in MERGE_HEAD.
|
||||
@@ -1351,7 +1351,7 @@ away, you can always return to the pre-merge state with
|
||||
$ git reset --hard HEAD
|
||||
-------------------------------------------------
|
||||
|
||||
Or, if you've already commited the merge that you want to throw away,
|
||||
Or, if you've already committed the merge that you want to throw away,
|
||||
|
||||
-------------------------------------------------
|
||||
$ git reset --hard ORIG_HEAD
|
||||
|
||||
7
INSTALL
7
INSTALL
@@ -56,9 +56,8 @@ Issues of note:
|
||||
|
||||
- "zlib", the compression library. Git won't build without it.
|
||||
|
||||
- "openssl". The git-rev-list program uses bignum support from
|
||||
openssl, and unless you specify otherwise, you'll also get the
|
||||
SHA1 library from here.
|
||||
- "openssl". Unless you specify otherwise, you'll get the SHA1
|
||||
library from here.
|
||||
|
||||
If you don't have openssl, you can use one of the SHA1 libraries
|
||||
that come with git (git includes the one from Mozilla, and has
|
||||
@@ -73,7 +72,7 @@ Issues of note:
|
||||
management over DAV. Similar to "curl" above, this is optional.
|
||||
|
||||
- "wish", the Tcl/Tk windowing shell is used in gitk to show the
|
||||
history graphically
|
||||
history graphically, and in git-gui.
|
||||
|
||||
- "ssh" is used to push and pull over the net
|
||||
|
||||
|
||||
5
Makefile
5
Makefile
@@ -176,6 +176,7 @@ CC = gcc
|
||||
AR = ar
|
||||
RM = rm -f
|
||||
TAR = tar
|
||||
FIND = find
|
||||
INSTALL = install
|
||||
RPMBUILD = rpmbuild
|
||||
TCL_PATH = tclsh
|
||||
@@ -934,11 +935,11 @@ doc:
|
||||
|
||||
TAGS:
|
||||
$(RM) TAGS
|
||||
find . -name '*.[hcS]' -print | xargs etags -a
|
||||
$(FIND) . -name '*.[hcS]' -print | xargs etags -a
|
||||
|
||||
tags:
|
||||
$(RM) tags
|
||||
find . -name '*.[hcS]' -print | xargs ctags -a
|
||||
$(FIND) . -name '*.[hcS]' -print | xargs ctags -a
|
||||
|
||||
### Detect prefix changes
|
||||
TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
|
||||
|
||||
@@ -60,7 +60,7 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec,
|
||||
path = git_path("info/exclude");
|
||||
if (!access(path, R_OK))
|
||||
add_excludes_from_file(dir, path);
|
||||
if (!access(excludes_file, R_OK))
|
||||
if (excludes_file != NULL && !access(excludes_file, R_OK))
|
||||
add_excludes_from_file(dir, excludes_file);
|
||||
}
|
||||
|
||||
|
||||
@@ -233,6 +233,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
|
||||
die("diff_setup_done failed");
|
||||
}
|
||||
rev.diffopt.allow_external = 1;
|
||||
rev.diffopt.recursive = 1;
|
||||
|
||||
/* Do we have --cached and not have a pending object, then
|
||||
* default to HEAD by hand. Eek.
|
||||
|
||||
@@ -152,7 +152,17 @@ static void check_unreachable_object(struct object *obj)
|
||||
}
|
||||
if (!(f = fopen(filename, "w")))
|
||||
die("Could not open %s", filename);
|
||||
fprintf(f, "%s\n", sha1_to_hex(obj->sha1));
|
||||
if (obj->type == OBJ_BLOB) {
|
||||
enum object_type type;
|
||||
unsigned long size;
|
||||
char *buf = read_sha1_file(obj->sha1,
|
||||
&type, &size);
|
||||
if (buf) {
|
||||
fwrite(buf, size, 1, f);
|
||||
free(buf);
|
||||
}
|
||||
} else
|
||||
fprintf(f, "%s\n", sha1_to_hex(obj->sha1));
|
||||
fclose(f);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -499,15 +499,42 @@ static int decode_b_segment(char *in, char *ot, char *ep)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* When there is no known charset, guess.
|
||||
*
|
||||
* Right now we assume that if the target is UTF-8 (the default),
|
||||
* and it already looks like UTF-8 (which includes US-ASCII as its
|
||||
* subset, of course) then that is what it is and there is nothing
|
||||
* to do.
|
||||
*
|
||||
* Otherwise, we default to assuming it is Latin1 for historical
|
||||
* reasons.
|
||||
*/
|
||||
static const char *guess_charset(const char *line, const char *target_charset)
|
||||
{
|
||||
if (is_encoding_utf8(target_charset)) {
|
||||
if (is_utf8(line))
|
||||
return NULL;
|
||||
}
|
||||
return "latin1";
|
||||
}
|
||||
|
||||
static void convert_to_utf8(char *line, const char *charset)
|
||||
{
|
||||
static const char latin_one[] = "latin1";
|
||||
const char *input_charset = *charset ? charset : latin_one;
|
||||
char *out = reencode_string(line, metainfo_charset, input_charset);
|
||||
char *out;
|
||||
|
||||
if (!charset || !*charset) {
|
||||
charset = guess_charset(line, metainfo_charset);
|
||||
if (!charset)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(metainfo_charset, charset))
|
||||
return;
|
||||
out = reencode_string(line, metainfo_charset, charset);
|
||||
if (!out)
|
||||
die("cannot convert from %s to %s\n",
|
||||
input_charset, metainfo_charset);
|
||||
charset, metainfo_charset);
|
||||
strcpy(line, out);
|
||||
free(out);
|
||||
}
|
||||
|
||||
@@ -79,8 +79,10 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
|
||||
|
||||
size = 1024;
|
||||
buffer = xmalloc(size);
|
||||
if (read_pipe(0, &buffer, &size))
|
||||
if (read_fd(0, &buffer, &size)) {
|
||||
free(buffer);
|
||||
die("could not read the input");
|
||||
}
|
||||
|
||||
size = stripspace(buffer, size, 0);
|
||||
write_or_die(1, buffer, size);
|
||||
|
||||
@@ -52,6 +52,8 @@ int write_tree(unsigned char *sha1, int missing_ok, const char *prefix)
|
||||
if (prefix) {
|
||||
struct cache_tree *subtree =
|
||||
cache_tree_find(active_cache_tree, prefix);
|
||||
if (!subtree)
|
||||
die("git-write-tree: prefix %s not found", prefix);
|
||||
hashcpy(sha1, subtree->sha1);
|
||||
}
|
||||
else
|
||||
|
||||
2
cache.h
2
cache.h
@@ -265,7 +265,7 @@ extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat
|
||||
extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);
|
||||
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
|
||||
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
|
||||
extern int read_pipe(int fd, char** return_buf, unsigned long* return_size);
|
||||
extern int read_fd(int fd, char **return_buf, unsigned long *return_size);
|
||||
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
|
||||
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
|
||||
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
|
||||
|
||||
5
commit.c
5
commit.c
@@ -721,7 +721,10 @@ static char *logmsg_reencode(const struct commit *commit,
|
||||
encoding = get_header(commit, "encoding");
|
||||
use_encoding = encoding ? encoding : utf8;
|
||||
if (!strcmp(use_encoding, output_encoding))
|
||||
out = xstrdup(commit->buffer);
|
||||
if (encoding) /* we'll strip encoding header later */
|
||||
out = xstrdup(commit->buffer);
|
||||
else
|
||||
return NULL; /* nothing to do */
|
||||
else
|
||||
out = reencode_string(commit->buffer,
|
||||
output_encoding, use_encoding);
|
||||
|
||||
@@ -13,7 +13,7 @@ all: $(ELC)
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d $(DESTDIR)$(emacsdir)
|
||||
$(INSTALL_ELC) $(ELC) $(DESTDIR)$(emacsdir)
|
||||
$(INSTALL_ELC) $(ELC:.elc=.el) $(ELC) $(DESTDIR)$(emacsdir)
|
||||
|
||||
%.elc: %.el
|
||||
$(EMACS) -batch -f batch-byte-compile $<
|
||||
|
||||
@@ -314,8 +314,8 @@ and returns the process output as a string."
|
||||
(sort-lines nil (point-min) (point-max))
|
||||
(save-buffer))
|
||||
(when created
|
||||
(git-run-command nil nil "update-index" "--info-only" "--add" "--" (file-relative-name ignore-name)))
|
||||
(git-add-status-file (if created 'added 'modified) (file-relative-name ignore-name))))
|
||||
(git-run-command nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
|
||||
(git-update-status-files (list (file-relative-name ignore-name)) 'unknown)))
|
||||
|
||||
; propertize definition for XEmacs, stolen from erc-compat
|
||||
(eval-when-compile
|
||||
@@ -523,23 +523,39 @@ and returns the process output as a string."
|
||||
" " (git-escape-file-name (git-fileinfo->name info))
|
||||
(git-rename-as-string info))))
|
||||
|
||||
(defun git-parse-status (status)
|
||||
"Parse the output of git-diff-index in the current buffer."
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward
|
||||
":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
|
||||
nil t 1)
|
||||
(let ((old-perm (string-to-number (match-string 1) 8))
|
||||
(new-perm (string-to-number (match-string 2) 8))
|
||||
(state (or (match-string 4) (match-string 6)))
|
||||
(name (or (match-string 5) (match-string 7)))
|
||||
(new-name (match-string 8)))
|
||||
(if new-name ; copy or rename
|
||||
(if (eq ?C (string-to-char state))
|
||||
(ewoc-enter-last status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name))
|
||||
(ewoc-enter-last status (git-create-fileinfo 'deleted name 0 0 'rename new-name))
|
||||
(ewoc-enter-last status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)))
|
||||
(ewoc-enter-last status (git-create-fileinfo (git-state-code state) name old-perm new-perm))))))
|
||||
(defun git-insert-fileinfo (status info &optional refresh)
|
||||
"Insert INFO in the status buffer, optionally refreshing an existing one."
|
||||
(let ((node (and refresh
|
||||
(git-find-status-file status (git-fileinfo->name info)))))
|
||||
(setf (git-fileinfo->needs-refresh info) t)
|
||||
(when node ;preserve the marked flag
|
||||
(setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))))
|
||||
(if node (ewoc-set-data node info) (ewoc-enter-last status info))))
|
||||
|
||||
(defun git-run-diff-index (status files)
|
||||
"Run git-diff-index on FILES and parse the results into STATUS.
|
||||
Return the list of files that haven't been handled."
|
||||
(let ((refresh files))
|
||||
(with-temp-buffer
|
||||
(apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward
|
||||
":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
|
||||
nil t 1)
|
||||
(let ((old-perm (string-to-number (match-string 1) 8))
|
||||
(new-perm (string-to-number (match-string 2) 8))
|
||||
(state (or (match-string 4) (match-string 6)))
|
||||
(name (or (match-string 5) (match-string 7)))
|
||||
(new-name (match-string 8)))
|
||||
(if new-name ; copy or rename
|
||||
(if (eq ?C (string-to-char state))
|
||||
(git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) refresh)
|
||||
(git-insert-fileinfo status (git-create-fileinfo 'deleted name 0 0 'rename new-name) refresh)
|
||||
(git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)) refresh)
|
||||
(git-insert-fileinfo status (git-create-fileinfo (git-state-code state) name old-perm new-perm) refresh))
|
||||
(setq files (delete name files))
|
||||
(when new-name (setq files (delete new-name files)))))))
|
||||
files)
|
||||
|
||||
(defun git-find-status-file (status file)
|
||||
"Find a given file in the status ewoc and return its node."
|
||||
@@ -548,32 +564,59 @@ and returns the process output as a string."
|
||||
(setq node (ewoc-next status node)))
|
||||
node))
|
||||
|
||||
(defun git-parse-ls-files (status default-state &optional skip-existing)
|
||||
"Parse the output of git-ls-files in the current buffer."
|
||||
(goto-char (point-min))
|
||||
(let (infolist)
|
||||
(while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1)
|
||||
(let ((state (match-string 1))
|
||||
(name (match-string 2)))
|
||||
(unless (and skip-existing (git-find-status-file status name))
|
||||
(push (git-create-fileinfo (or (git-state-code state) default-state) name) infolist))))
|
||||
(dolist (info (nreverse infolist))
|
||||
(ewoc-enter-last status info))))
|
||||
(defun git-run-ls-files (status files default-state &rest options)
|
||||
"Run git-ls-files on FILES and parse the results into STATUS.
|
||||
Return the list of files that haven't been handled."
|
||||
(let ((refresh files))
|
||||
(with-temp-buffer
|
||||
(apply #'git-run-command t nil "ls-files" "-z" "-t" (append options (list "--") files))
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1)
|
||||
(let ((state (match-string 1))
|
||||
(name (match-string 2)))
|
||||
(git-insert-fileinfo status (git-create-fileinfo (or (git-state-code state) default-state) name) refresh)
|
||||
(setq files (delete name files))))))
|
||||
files)
|
||||
|
||||
(defun git-parse-ls-unmerged (status)
|
||||
"Parse the output of git-ls-files -u in the current buffer."
|
||||
(goto-char (point-min))
|
||||
(let (files)
|
||||
(while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
|
||||
(let ((node (git-find-status-file status (match-string 1))))
|
||||
(when node (push (ewoc-data node) files))))
|
||||
(git-set-files-state files 'unmerged)))
|
||||
(defun git-run-ls-unmerged (status files)
|
||||
"Run git-ls-files -u on FILES and parse the results into STATUS."
|
||||
(with-temp-buffer
|
||||
(apply #'git-run-command t nil "ls-files" "-z" "-u" "--" files)
|
||||
(goto-char (point-min))
|
||||
(let (unmerged-files)
|
||||
(while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
|
||||
(let ((node (git-find-status-file status (match-string 1))))
|
||||
(when node (push (ewoc-data node) unmerged-files))))
|
||||
(git-set-files-state unmerged-files 'unmerged))))
|
||||
|
||||
(defun git-add-status-file (state name)
|
||||
"Add a new file to the status list (if not existing already) and return its node."
|
||||
(defun git-update-status-files (files &optional default-state)
|
||||
"Update the status of FILES from the index."
|
||||
(unless git-status (error "Not in git-status buffer."))
|
||||
(or (git-find-status-file git-status name)
|
||||
(ewoc-enter-last git-status (git-create-fileinfo state name))))
|
||||
(let* ((status git-status)
|
||||
(remaining-files
|
||||
(if (git-empty-db-p) ; we need some special handling for an empty db
|
||||
(git-run-ls-files status files 'added "-c")
|
||||
(git-run-diff-index status files))))
|
||||
(git-run-ls-unmerged status files)
|
||||
(when (and (or (not files) remaining-files)
|
||||
(file-readable-p ".git/info/exclude"))
|
||||
(setq remaining-files (git-run-ls-files status remaining-files
|
||||
'unknown "-o" "--exclude-from=.git/info/exclude"
|
||||
(concat "--exclude-per-directory=" git-per-dir-ignore-file))))
|
||||
; mark remaining files with the default state (or remove them if nil)
|
||||
(when remaining-files
|
||||
(if default-state
|
||||
(ewoc-map (lambda (info)
|
||||
(when (member (git-fileinfo->name info) remaining-files)
|
||||
(git-set-files-state (list info) default-state))
|
||||
nil)
|
||||
status)
|
||||
(ewoc-filter status
|
||||
(lambda (info files)
|
||||
(not (member (git-fileinfo->name info) files)))
|
||||
remaining-files)))
|
||||
(git-refresh-files)
|
||||
(git-refresh-ewoc-hf status)))
|
||||
|
||||
(defun git-marked-files ()
|
||||
"Return a list of all marked files, or if none a list containing just the file at cursor position."
|
||||
@@ -789,54 +832,34 @@ and returns the process output as a string."
|
||||
(defun git-add-file ()
|
||||
"Add marked file(s) to the index cache."
|
||||
(interactive)
|
||||
(let ((files (git-marked-files-state 'unknown)))
|
||||
(let ((files (git-get-filenames (git-marked-files-state 'unknown))))
|
||||
(unless files
|
||||
(push (ewoc-data
|
||||
(git-add-status-file 'added (file-relative-name
|
||||
(read-file-name "File to add: " nil nil t))))
|
||||
files))
|
||||
(apply #'git-run-command nil nil "update-index" "--info-only" "--add" "--" (git-get-filenames files))
|
||||
(git-set-files-state files 'added)
|
||||
(git-refresh-files)))
|
||||
(push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
|
||||
(apply #'git-run-command nil nil "update-index" "--add" "--" files)
|
||||
(git-update-status-files files 'uptodate)))
|
||||
|
||||
(defun git-ignore-file ()
|
||||
"Add marked file(s) to the ignore list."
|
||||
(interactive)
|
||||
(let ((files (git-marked-files-state 'unknown)))
|
||||
(let ((files (git-get-filenames (git-marked-files-state 'unknown))))
|
||||
(unless files
|
||||
(push (ewoc-data
|
||||
(git-add-status-file 'unknown (file-relative-name
|
||||
(read-file-name "File to ignore: " nil nil t))))
|
||||
files))
|
||||
(dolist (info files) (git-append-to-ignore (git-fileinfo->name info)))
|
||||
(git-set-files-state files 'ignored)
|
||||
(git-refresh-files)))
|
||||
(push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files))
|
||||
(dolist (f files) (git-append-to-ignore f))
|
||||
(git-update-status-files files 'ignored)))
|
||||
|
||||
(defun git-remove-file ()
|
||||
"Remove the marked file(s)."
|
||||
(interactive)
|
||||
(let ((files (git-marked-files-state 'added 'modified 'unknown 'uptodate)))
|
||||
(let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate))))
|
||||
(unless files
|
||||
(push (ewoc-data
|
||||
(git-add-status-file 'unknown (file-relative-name
|
||||
(read-file-name "File to remove: " nil nil t))))
|
||||
files))
|
||||
(push (file-relative-name (read-file-name "File to remove: " nil nil t)) files))
|
||||
(if (yes-or-no-p
|
||||
(format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" "")))
|
||||
(progn
|
||||
(dolist (info files)
|
||||
(let ((name (git-fileinfo->name info)))
|
||||
(when (file-exists-p name) (delete-file name))))
|
||||
(apply #'git-run-command nil nil "update-index" "--info-only" "--remove" "--" (git-get-filenames files))
|
||||
; remove unknown files from the list, set the others to deleted
|
||||
(ewoc-filter git-status
|
||||
(lambda (info files)
|
||||
(not (and (memq info files) (eq (git-fileinfo->state info) 'unknown))))
|
||||
files)
|
||||
(git-set-files-state files 'deleted)
|
||||
(git-refresh-files)
|
||||
(unless (ewoc-nth git-status 0) ; refresh header if list is empty
|
||||
(git-refresh-ewoc-hf git-status)))
|
||||
(dolist (name files)
|
||||
(when (file-exists-p name) (delete-file name)))
|
||||
(apply #'git-run-command nil nil "update-index" "--remove" "--" files)
|
||||
(git-update-status-files files nil))
|
||||
(message "Aborting"))))
|
||||
|
||||
(defun git-revert-file ()
|
||||
@@ -849,26 +872,23 @@ and returns the process output as a string."
|
||||
(format "Revert %d file%s? " (length files) (if (> (length files) 1) "s" ""))))
|
||||
(dolist (info files)
|
||||
(case (git-fileinfo->state info)
|
||||
('added (push info added))
|
||||
('deleted (push info modified))
|
||||
('unmerged (push info modified))
|
||||
('modified (push info modified))))
|
||||
('added (push (git-fileinfo->name info) added))
|
||||
('deleted (push (git-fileinfo->name info) modified))
|
||||
('unmerged (push (git-fileinfo->name info) modified))
|
||||
('modified (push (git-fileinfo->name info) modified))))
|
||||
(when added
|
||||
(apply #'git-run-command nil nil "update-index" "--force-remove" "--" (git-get-filenames added))
|
||||
(git-set-files-state added 'unknown))
|
||||
(apply #'git-run-command nil nil "update-index" "--force-remove" "--" added))
|
||||
(when modified
|
||||
(apply #'git-run-command nil nil "checkout" "HEAD" (git-get-filenames modified))
|
||||
(git-set-files-state modified 'uptodate))
|
||||
(git-refresh-files))))
|
||||
(apply #'git-run-command nil nil "checkout" "HEAD" modified))
|
||||
(git-update-status-files (append added modified) 'uptodate))))
|
||||
|
||||
(defun git-resolve-file ()
|
||||
"Resolve conflicts in marked file(s)."
|
||||
(interactive)
|
||||
(let ((files (git-marked-files-state 'unmerged)))
|
||||
(let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
|
||||
(when files
|
||||
(apply #'git-run-command nil nil "update-index" "--" (git-get-filenames files))
|
||||
(git-set-files-state files 'modified)
|
||||
(git-refresh-files))))
|
||||
(apply #'git-run-command nil nil "update-index" "--" files)
|
||||
(git-update-status-files files 'uptodate))))
|
||||
|
||||
(defun git-remove-handled ()
|
||||
"Remove handled files from the status list."
|
||||
@@ -1038,7 +1058,7 @@ and returns the process output as a string."
|
||||
(let ((info (ewoc-data (ewoc-locate git-status))))
|
||||
(find-file (git-fileinfo->name info))
|
||||
(when (eq 'unmerged (git-fileinfo->state info))
|
||||
(smerge-mode))))
|
||||
(smerge-mode 1))))
|
||||
|
||||
(defun git-find-file-other-window ()
|
||||
"Visit the current file in its own buffer in another window."
|
||||
@@ -1071,27 +1091,9 @@ and returns the process output as a string."
|
||||
(pos (ewoc-locate status))
|
||||
(cur-name (and pos (git-fileinfo->name (ewoc-data pos)))))
|
||||
(unless status (error "Not in git-status buffer."))
|
||||
(git-run-command nil nil "update-index" "--refresh")
|
||||
(git-clear-status status)
|
||||
(git-run-command nil nil "update-index" "--info-only" "--refresh")
|
||||
(if (git-empty-db-p)
|
||||
; we need some special handling for an empty db
|
||||
(with-temp-buffer
|
||||
(git-run-command t nil "ls-files" "-z" "-t" "-c")
|
||||
(git-parse-ls-files status 'added))
|
||||
(with-temp-buffer
|
||||
(git-run-command t nil "diff-index" "-z" "-M" "HEAD")
|
||||
(git-parse-status status)))
|
||||
(with-temp-buffer
|
||||
(git-run-command t nil "ls-files" "-z" "-u")
|
||||
(git-parse-ls-unmerged status))
|
||||
(when (file-readable-p ".git/info/exclude")
|
||||
(with-temp-buffer
|
||||
(git-run-command t nil "ls-files" "-z" "-t" "-o"
|
||||
"--exclude-from=.git/info/exclude"
|
||||
(concat "--exclude-per-directory=" git-per-dir-ignore-file))
|
||||
(git-parse-ls-files status 'unknown)))
|
||||
(git-refresh-files)
|
||||
(git-refresh-ewoc-hf status)
|
||||
(git-update-status-files nil)
|
||||
; move point to the current file name if any
|
||||
(let ((node (and cur-name (git-find-status-file status cur-name))))
|
||||
(when node (ewoc-goto-node status node)))))
|
||||
|
||||
@@ -63,21 +63,34 @@ def system(cmd):
|
||||
if os.system(cmd) != 0:
|
||||
die("command failed: %s" % cmd)
|
||||
|
||||
def p4CmdList(cmd):
|
||||
def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
|
||||
cmd = "p4 -G %s" % cmd
|
||||
if verbose:
|
||||
sys.stderr.write("Opening pipe: %s\n" % cmd)
|
||||
pipe = os.popen(cmd, "rb")
|
||||
|
||||
# Use a temporary file to avoid deadlocks without
|
||||
# subprocess.communicate(), which would put another copy
|
||||
# of stdout into memory.
|
||||
stdin_file = None
|
||||
if stdin is not None:
|
||||
stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode)
|
||||
stdin_file.write(stdin)
|
||||
stdin_file.flush()
|
||||
stdin_file.seek(0)
|
||||
|
||||
p4 = subprocess.Popen(cmd, shell=True,
|
||||
stdin=stdin_file,
|
||||
stdout=subprocess.PIPE)
|
||||
|
||||
result = []
|
||||
try:
|
||||
while True:
|
||||
entry = marshal.load(pipe)
|
||||
entry = marshal.load(p4.stdout)
|
||||
result.append(entry)
|
||||
except EOFError:
|
||||
pass
|
||||
exitCode = pipe.close()
|
||||
if exitCode != None:
|
||||
exitCode = p4.wait()
|
||||
if exitCode != 0:
|
||||
entry = {}
|
||||
entry["p4ExitCode"] = exitCode
|
||||
result.append(entry)
|
||||
@@ -168,27 +181,55 @@ def gitBranchExists(branch):
|
||||
def gitConfig(key):
|
||||
return read_pipe("git config %s" % key, ignore_error=True).strip()
|
||||
|
||||
def p4BranchesInGit(branchesAreInRemotes = True):
|
||||
branches = {}
|
||||
|
||||
cmdline = "git rev-parse --symbolic "
|
||||
if branchesAreInRemotes:
|
||||
cmdline += " --remotes"
|
||||
else:
|
||||
cmdline += " --branches"
|
||||
|
||||
for line in read_pipe_lines(cmdline):
|
||||
line = line.strip()
|
||||
|
||||
## only import to p4/
|
||||
if not line.startswith('p4/') or line == "p4/HEAD":
|
||||
continue
|
||||
branch = line
|
||||
|
||||
# strip off p4
|
||||
branch = re.sub ("^p4/", "", line)
|
||||
|
||||
branches[branch] = parseRevision(line)
|
||||
return branches
|
||||
|
||||
def findUpstreamBranchPoint(head = "HEAD"):
|
||||
branches = p4BranchesInGit()
|
||||
# map from depot-path to branch name
|
||||
branchByDepotPath = {}
|
||||
for branch in branches.keys():
|
||||
tip = branches[branch]
|
||||
log = extractLogMessageFromGitCommit(tip)
|
||||
settings = extractSettingsGitLog(log)
|
||||
if settings.has_key("depot-paths"):
|
||||
paths = ",".join(settings["depot-paths"])
|
||||
branchByDepotPath[paths] = "remotes/p4/" + branch
|
||||
|
||||
settings = None
|
||||
branchPoint = ""
|
||||
parent = 0
|
||||
while parent < 65535:
|
||||
commit = head + "~%s" % parent
|
||||
log = extractLogMessageFromGitCommit(commit)
|
||||
settings = extractSettingsGitLog(log)
|
||||
if not settings.has_key("depot-paths"):
|
||||
parent = parent + 1
|
||||
continue
|
||||
if settings.has_key("depot-paths"):
|
||||
paths = ",".join(settings["depot-paths"])
|
||||
if branchByDepotPath.has_key(paths):
|
||||
return [branchByDepotPath[paths], settings]
|
||||
|
||||
names = read_pipe_lines("git name-rev \"--refs=refs/remotes/p4/*\" \"%s\"" % commit)
|
||||
if len(names) <= 0:
|
||||
continue
|
||||
parent = parent + 1
|
||||
|
||||
# strip away the beginning of 'HEAD~42 refs/remotes/p4/foo'
|
||||
branchPoint = names[0].strip()[len(commit) + 1:]
|
||||
break
|
||||
|
||||
return [branchPoint, settings]
|
||||
return ["", settings]
|
||||
|
||||
class Command:
|
||||
def __init__(self):
|
||||
@@ -712,27 +753,13 @@ class P4Sync(Command):
|
||||
if not files:
|
||||
return
|
||||
|
||||
# We cannot put all the files on the command line
|
||||
# OS have limitations on the max lenght of arguments
|
||||
# POSIX says it's 4096 bytes, default for Linux seems to be 130 K.
|
||||
# and all OS from the table below seems to be higher than POSIX.
|
||||
# See http://www.in-ulm.de/~mascheck/various/argmax/
|
||||
if (self.isWindows):
|
||||
argmax = 2000
|
||||
else:
|
||||
argmax = min(4000, os.sysconf('SC_ARG_MAX'))
|
||||
|
||||
chunk = ''
|
||||
filedata = []
|
||||
for i in xrange(len(files)):
|
||||
f = files[i]
|
||||
chunk += '"%s#%s" ' % (f['path'], f['rev'])
|
||||
if len(chunk) > argmax or i == len(files)-1:
|
||||
data = p4CmdList('print %s' % chunk)
|
||||
if "p4ExitCode" in data[0]:
|
||||
die("Problems executing p4. Error: [%d]." % (data[0]['p4ExitCode']));
|
||||
filedata.extend(data)
|
||||
chunk = ''
|
||||
filedata = p4CmdList('-x - print',
|
||||
stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
|
||||
for f in files]),
|
||||
stdin_mode='w+')
|
||||
if "p4ExitCode" in filedata[0]:
|
||||
die("Problems executing p4. Error: [%d]."
|
||||
% (filedata[0]['p4ExitCode']));
|
||||
|
||||
j = 0;
|
||||
contents = {}
|
||||
@@ -874,7 +901,8 @@ class P4Sync(Command):
|
||||
% (labelDetails["label"], change))
|
||||
|
||||
def getUserCacheFilename(self):
|
||||
return os.environ["HOME"] + "/.gitp4-usercache.txt"
|
||||
home = os.environ.get("HOME", os.environ.get("USERPROFILE"))
|
||||
return home + "/.gitp4-usercache.txt"
|
||||
|
||||
def getUserMapFromPerforceServer(self):
|
||||
if self.userMapFromPerforceServer:
|
||||
@@ -979,27 +1007,11 @@ class P4Sync(Command):
|
||||
self.knownBranches[branch] = branch
|
||||
|
||||
def listExistingP4GitBranches(self):
|
||||
self.p4BranchesInGit = []
|
||||
|
||||
cmdline = "git rev-parse --symbolic "
|
||||
if self.importIntoRemotes:
|
||||
cmdline += " --remotes"
|
||||
else:
|
||||
cmdline += " --branches"
|
||||
|
||||
for line in read_pipe_lines(cmdline):
|
||||
line = line.strip()
|
||||
|
||||
## only import to p4/
|
||||
if not line.startswith('p4/') or line == "p4/HEAD":
|
||||
continue
|
||||
branch = line
|
||||
|
||||
# strip off p4
|
||||
branch = re.sub ("^p4/", "", line)
|
||||
|
||||
self.p4BranchesInGit.append(branch)
|
||||
self.initialParents[self.refPrefix + branch] = parseRevision(line)
|
||||
# branches holds mapping from name to commit
|
||||
branches = p4BranchesInGit(self.importIntoRemotes)
|
||||
self.p4BranchesInGit = branches.keys()
|
||||
for branch in branches.keys():
|
||||
self.initialParents[self.refPrefix + branch] = branches[branch]
|
||||
|
||||
def createOrUpdateBranchesFromOrigin(self):
|
||||
if not self.silent:
|
||||
|
||||
9
date.c
9
date.c
@@ -660,6 +660,14 @@ static void date_am(struct tm *tm, int *num)
|
||||
tm->tm_hour = (hour % 12);
|
||||
}
|
||||
|
||||
static void date_never(struct tm *tm, int *num)
|
||||
{
|
||||
tm->tm_mon = tm->tm_wday = tm->tm_yday
|
||||
= tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
|
||||
tm->tm_year = 70;
|
||||
tm->tm_mday = 1;
|
||||
}
|
||||
|
||||
static const struct special {
|
||||
const char *name;
|
||||
void (*fn)(struct tm *, int *);
|
||||
@@ -670,6 +678,7 @@ static const struct special {
|
||||
{ "tea", date_tea },
|
||||
{ "PM", date_pm },
|
||||
{ "AM", date_am },
|
||||
{ "never", date_never },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
||||
2
diff.c
2
diff.c
@@ -1696,7 +1696,7 @@ static void prep_temp_blob(struct diff_tempfile *temp,
|
||||
|
||||
fd = git_mkstemp(temp->tmp_path, PATH_MAX, ".diff_XXXXXX");
|
||||
if (fd < 0)
|
||||
die("unable to create temp-file");
|
||||
die("unable to create temp-file: %s", strerror(errno));
|
||||
if (write_in_full(fd, blob, size) != size)
|
||||
die("unable to write temp-file");
|
||||
close(fd);
|
||||
|
||||
37
entry.c
37
entry.c
@@ -8,17 +8,40 @@ static void create_directories(const char *path, const struct checkout *state)
|
||||
const char *slash = path;
|
||||
|
||||
while ((slash = strchr(slash+1, '/')) != NULL) {
|
||||
struct stat st;
|
||||
int stat_status;
|
||||
|
||||
len = slash - path;
|
||||
memcpy(buf, path, len);
|
||||
buf[len] = 0;
|
||||
|
||||
if (len <= state->base_dir_len)
|
||||
/*
|
||||
* checkout-index --prefix=<dir>; <dir> is
|
||||
* allowed to be a symlink to an existing
|
||||
* directory.
|
||||
*/
|
||||
stat_status = stat(buf, &st);
|
||||
else
|
||||
/*
|
||||
* if there currently is a symlink, we would
|
||||
* want to replace it with a real directory.
|
||||
*/
|
||||
stat_status = lstat(buf, &st);
|
||||
|
||||
if (!stat_status && S_ISDIR(st.st_mode))
|
||||
continue; /* ok, it is already a directory. */
|
||||
|
||||
/*
|
||||
* We know stat_status == 0 means something exists
|
||||
* there and this mkdir would fail, but that is an
|
||||
* error codepath; we do not care, as we unlink and
|
||||
* mkdir again in such a case.
|
||||
*/
|
||||
if (mkdir(buf, 0777)) {
|
||||
if (errno == EEXIST) {
|
||||
struct stat st;
|
||||
if (len > state->base_dir_len && state->force && !unlink(buf) && !mkdir(buf, 0777))
|
||||
continue;
|
||||
if (!stat(buf, &st) && S_ISDIR(st.st_mode))
|
||||
continue; /* ok */
|
||||
}
|
||||
if (errno == EEXIST && state->force &&
|
||||
!unlink(buf) && !mkdir(buf, 0777))
|
||||
continue;
|
||||
die("cannot create directory at %s", buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,6 +284,12 @@ do
|
||||
git mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
|
||||
<"$dotest/$msgnum" >"$dotest/info" ||
|
||||
stop_here $this
|
||||
|
||||
# skip pine's internal folder data
|
||||
grep '^Author: Mail System Internal Data$' \
|
||||
<"$dotest"/info >/dev/null &&
|
||||
go_next && continue
|
||||
|
||||
test -s $dotest/patch || {
|
||||
echo "Patch is empty. Was it split wrong?"
|
||||
stop_here $this
|
||||
@@ -364,7 +370,7 @@ do
|
||||
[yY]*) action=yes ;;
|
||||
[aA]*) action=yes interactive= ;;
|
||||
[nN]*) action=skip ;;
|
||||
[eE]*) "${VISUAL:-${EDITOR:-vi}}" "$dotest/final-commit"
|
||||
[eE]*) git_editor "$dotest/final-commit"
|
||||
action=again ;;
|
||||
[vV]*) action=again
|
||||
LESS=-S ${PAGER:-less} "$dotest/patch" ;;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Copyright (c) 2005 Linus Torvalds
|
||||
# Copyright (c) 2006 Junio C Hamano
|
||||
|
||||
USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [[-i | -o] <path>...]'
|
||||
USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]'
|
||||
SUBDIRECTORY_OK=Yes
|
||||
. git-sh-setup
|
||||
require_work_tree
|
||||
@@ -87,6 +87,7 @@ signoff=
|
||||
force_author=
|
||||
only_include_assumed=
|
||||
untracked_files=
|
||||
templatefile="`git config commit.template`"
|
||||
while case "$#" in 0) break;; esac
|
||||
do
|
||||
case "$1" in
|
||||
@@ -248,6 +249,13 @@ $1"
|
||||
signoff=t
|
||||
shift
|
||||
;;
|
||||
-t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
templatefile="$1"
|
||||
no_edit=
|
||||
shift
|
||||
;;
|
||||
-q|--q|--qu|--qui|--quie|--quiet)
|
||||
quiet=t
|
||||
shift
|
||||
@@ -321,6 +329,14 @@ t,,[1-9]*)
|
||||
die "No paths with -i does not make sense." ;;
|
||||
esac
|
||||
|
||||
if test ! -z "$templatefile" -a -z "$log_given"
|
||||
then
|
||||
if test ! -f "$templatefile"
|
||||
then
|
||||
die "Commit template file does not exist."
|
||||
fi
|
||||
fi
|
||||
|
||||
################################################################
|
||||
# Prepare index to have a tree to be committed
|
||||
|
||||
@@ -454,6 +470,9 @@ then
|
||||
elif test -f "$GIT_DIR/SQUASH_MSG"
|
||||
then
|
||||
cat "$GIT_DIR/SQUASH_MSG"
|
||||
elif test "$templatefile" != ""
|
||||
then
|
||||
cat "$templatefile"
|
||||
fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG
|
||||
|
||||
case "$signoff" in
|
||||
@@ -544,18 +563,9 @@ fi
|
||||
|
||||
case "$no_edit" in
|
||||
'')
|
||||
case "${VISUAL:-$EDITOR},$TERM" in
|
||||
,dumb)
|
||||
echo >&2 "Terminal is dumb but no VISUAL nor EDITOR defined."
|
||||
echo >&2 "Please supply the commit log message using either"
|
||||
echo >&2 "-m or -F option. A boilerplate log message has"
|
||||
echo >&2 "been prepared in $GIT_DIR/COMMIT_EDITMSG"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
git-var GIT_AUTHOR_IDENT > /dev/null || die
|
||||
git-var GIT_COMMITTER_IDENT > /dev/null || die
|
||||
${VISUAL:-${EDITOR:-vi}} "$GIT_DIR/COMMIT_EDITMSG"
|
||||
git_editor "$GIT_DIR/COMMIT_EDITMSG"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -581,10 +591,35 @@ else
|
||||
fi |
|
||||
git stripspace >"$GIT_DIR"/COMMIT_MSG
|
||||
|
||||
if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
|
||||
git stripspace |
|
||||
wc -l` &&
|
||||
test 0 -lt $cnt
|
||||
# Test whether the commit message has any content we didn't supply.
|
||||
have_commitmsg=
|
||||
grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
|
||||
git stripspace > "$GIT_DIR"/COMMIT_BAREMSG
|
||||
|
||||
# Is the commit message totally empty?
|
||||
if test -s "$GIT_DIR"/COMMIT_BAREMSG
|
||||
then
|
||||
if test "$templatefile" != ""
|
||||
then
|
||||
# Test whether this is just the unaltered template.
|
||||
if cnt=`sed -e '/^#/d' < "$templatefile" |
|
||||
git stripspace |
|
||||
diff "$GIT_DIR"/COMMIT_BAREMSG - |
|
||||
wc -l` &&
|
||||
test 0 -lt $cnt
|
||||
then
|
||||
have_commitmsg=t
|
||||
fi
|
||||
else
|
||||
# No template, so the content in the commit message must
|
||||
# have come from the user.
|
||||
have_commitmsg=t
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f "$GIT_DIR"/COMMIT_BAREMSG
|
||||
|
||||
if test "$have_commitmsg" = "t"
|
||||
then
|
||||
if test -z "$TMP_INDEX"
|
||||
then
|
||||
|
||||
@@ -281,6 +281,11 @@ if ($opt_c) {
|
||||
# clean up
|
||||
unlink(".cvsexportcommit.diff");
|
||||
|
||||
# CVS version 1.11.x and 1.12.x sleeps the wrong way to ensure the timestamp
|
||||
# used by CVS and the one set by subsequence file modifications are different.
|
||||
# If they are not different CVS will not detect changes.
|
||||
sleep(1);
|
||||
|
||||
sub usage {
|
||||
print STDERR <<END;
|
||||
Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-m msgprefix] [ parent ] commit
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
# a new branch. You can specify a number of filters to modify the commits,
|
||||
# files and trees.
|
||||
|
||||
set -e
|
||||
|
||||
USAGE="git-filter-branch [-d TEMPDIR] [FILTERS] DESTBRANCH [REV-RANGE]"
|
||||
. git-sh-setup
|
||||
|
||||
@@ -80,6 +78,8 @@ filter_msg=cat
|
||||
filter_commit='git commit-tree "$@"'
|
||||
filter_tag_name=
|
||||
filter_subdir=
|
||||
orig_namespace=refs/original/
|
||||
force=
|
||||
while case "$#" in 0) usage;; esac
|
||||
do
|
||||
case "$1" in
|
||||
@@ -87,6 +87,11 @@ do
|
||||
shift
|
||||
break
|
||||
;;
|
||||
--force|-f)
|
||||
shift
|
||||
force=t
|
||||
continue
|
||||
;;
|
||||
-*)
|
||||
;;
|
||||
*)
|
||||
@@ -128,22 +133,42 @@ do
|
||||
--subdirectory-filter)
|
||||
filter_subdir="$OPTARG"
|
||||
;;
|
||||
--original)
|
||||
orig_namespace="$OPTARG"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
dstbranch="$1"
|
||||
shift
|
||||
test -n "$dstbranch" || die "missing branch name"
|
||||
git show-ref "refs/heads/$dstbranch" 2> /dev/null &&
|
||||
die "branch $dstbranch already exists"
|
||||
case "$force" in
|
||||
t)
|
||||
rm -rf "$tempdir"
|
||||
;;
|
||||
'')
|
||||
test -d "$tempdir" &&
|
||||
die "$tempdir already exists, please remove it"
|
||||
esac
|
||||
mkdir -p "$tempdir/t" &&
|
||||
tempdir="$(cd "$tempdir"; pwd)" &&
|
||||
cd "$tempdir/t" &&
|
||||
workdir="$(pwd)" ||
|
||||
die ""
|
||||
|
||||
test ! -e "$tempdir" || die "$tempdir already exists, please remove it"
|
||||
mkdir -p "$tempdir/t"
|
||||
cd "$tempdir/t"
|
||||
workdir="$(pwd)"
|
||||
# Make sure refs/original is empty
|
||||
git for-each-ref > "$tempdir"/backup-refs
|
||||
while read sha1 type name
|
||||
do
|
||||
case "$force,$name" in
|
||||
,$orig_namespace*)
|
||||
die "Namespace $orig_namespace not empty"
|
||||
;;
|
||||
t,$orig_namespace*)
|
||||
git update-ref -d "$name" $sha1
|
||||
;;
|
||||
esac
|
||||
done < "$tempdir"/backup-refs
|
||||
|
||||
case "$GIT_DIR" in
|
||||
/*)
|
||||
@@ -154,13 +179,36 @@ case "$GIT_DIR" in
|
||||
esac
|
||||
export GIT_DIR GIT_WORK_TREE=.
|
||||
|
||||
# These refs should be updated if their heads were rewritten
|
||||
|
||||
git rev-parse --revs-only --symbolic "$@" |
|
||||
while read ref
|
||||
do
|
||||
# normalize ref
|
||||
case "$ref" in
|
||||
HEAD)
|
||||
ref="$(git symbolic-ref "$ref")"
|
||||
;;
|
||||
refs/*)
|
||||
;;
|
||||
*)
|
||||
ref="$(git for-each-ref --format='%(refname)' |
|
||||
grep /"$ref")"
|
||||
esac
|
||||
|
||||
git check-ref-format "$ref" && echo "$ref"
|
||||
done > "$tempdir"/heads
|
||||
|
||||
test -s "$tempdir"/heads ||
|
||||
die "Which ref do you want to rewrite?"
|
||||
|
||||
export GIT_INDEX_FILE="$(pwd)/../index"
|
||||
git read-tree # seed the index file
|
||||
git read-tree || die "Could not seed the index"
|
||||
|
||||
ret=0
|
||||
|
||||
|
||||
mkdir ../map # map old->new commit ids for rewriting parents
|
||||
# map old->new commit ids for rewriting parents
|
||||
mkdir ../map || die "Could not create map/ directory"
|
||||
|
||||
case "$filter_subdir" in
|
||||
"")
|
||||
@@ -170,11 +218,13 @@ case "$filter_subdir" in
|
||||
*)
|
||||
git rev-list --reverse --topo-order --default HEAD \
|
||||
--parents --full-history "$@" -- "$filter_subdir"
|
||||
esac > ../revs
|
||||
esac > ../revs || die "Could not get the commits"
|
||||
commits=$(wc -l <../revs | tr -d " ")
|
||||
|
||||
test $commits -eq 0 && die "Found nothing to rewrite"
|
||||
|
||||
# Rewrite the commits
|
||||
|
||||
i=0
|
||||
while read commit parents; do
|
||||
i=$(($i+1))
|
||||
@@ -186,10 +236,11 @@ while read commit parents; do
|
||||
;;
|
||||
*)
|
||||
git read-tree -i -m $commit:"$filter_subdir"
|
||||
esac
|
||||
esac || die "Could not initialize the index"
|
||||
|
||||
export GIT_COMMIT=$commit
|
||||
git cat-file commit "$commit" >../commit
|
||||
git cat-file commit "$commit" >../commit ||
|
||||
die "Cannot read commit $commit"
|
||||
|
||||
eval "$(set_ident AUTHOR <../commit)" ||
|
||||
die "setting author failed for commit $commit"
|
||||
@@ -199,7 +250,8 @@ while read commit parents; do
|
||||
die "env filter failed: $filter_env"
|
||||
|
||||
if [ "$filter_tree" ]; then
|
||||
git checkout-index -f -u -a
|
||||
git checkout-index -f -u -a ||
|
||||
die "Could not checkout the index"
|
||||
# files that $commit removed are now still in the working tree;
|
||||
# remove them, else they would be added again
|
||||
git ls-files -z --others | xargs -0 rm -f
|
||||
@@ -233,21 +285,75 @@ while read commit parents; do
|
||||
$(git write-tree) $parentstr < ../message > ../map/$commit
|
||||
done <../revs
|
||||
|
||||
src_head=$(tail -n 1 ../revs | sed -e 's/ .*//')
|
||||
target_head=$(head -n 1 ../map/$src_head)
|
||||
case "$target_head" in
|
||||
'')
|
||||
echo Nothing rewritten
|
||||
# In case of a subdirectory filter, it is possible that a specified head
|
||||
# is not in the set of rewritten commits, because it was pruned by the
|
||||
# revision walker. Fix it by mapping these heads to the next rewritten
|
||||
# ancestor(s), i.e. the boundaries in the set of rewritten commits.
|
||||
|
||||
# NEEDSWORK: we should sort the unmapped refs topologically first
|
||||
while read ref
|
||||
do
|
||||
sha1=$(git rev-parse "$ref"^0)
|
||||
test -f "$workdir"/../map/$sha1 && continue
|
||||
# Assign the boundarie(s) in the set of rewritten commits
|
||||
# as the replacement commit(s).
|
||||
# (This would look a bit nicer if --not --stdin worked.)
|
||||
for p in $( (cd "$workdir"/../map; ls | sed "s/^/^/") |
|
||||
git rev-list $ref --boundary --stdin |
|
||||
sed -n "s/^-//p")
|
||||
do
|
||||
map $p >> "$workdir"/../map/$sha1
|
||||
done
|
||||
done < "$tempdir"/heads
|
||||
|
||||
# Finally update the refs
|
||||
|
||||
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
|
||||
_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
|
||||
count=0
|
||||
echo
|
||||
while read ref
|
||||
do
|
||||
# avoid rewriting a ref twice
|
||||
test -f "$orig_namespace$ref" && continue
|
||||
|
||||
sha1=$(git rev-parse "$ref"^0)
|
||||
rewritten=$(map $sha1)
|
||||
|
||||
test $sha1 = "$rewritten" &&
|
||||
warn "WARNING: Ref '$ref' is unchanged" &&
|
||||
continue
|
||||
|
||||
case "$rewritten" in
|
||||
'')
|
||||
echo "Ref '$ref' was deleted"
|
||||
git update-ref -m "filter-branch: delete" -d "$ref" $sha1 ||
|
||||
die "Could not delete $ref"
|
||||
;;
|
||||
*)
|
||||
git update-ref refs/heads/"$dstbranch" $target_head
|
||||
if [ $(wc -l <../map/$src_head) -gt 1 ]; then
|
||||
echo "WARNING: Your commit filter caused the head commit to expand to several rewritten commits. Only the first such commit was recorded as the current $dstbranch head but you will need to resolve the situation now (probably by manually merging the other commits). These are all the commits:" >&2
|
||||
sed 's/^/ /' ../map/$src_head >&2
|
||||
ret=1
|
||||
fi
|
||||
$_x40)
|
||||
echo "Ref '$ref' was rewritten"
|
||||
git update-ref -m "filter-branch: rewrite" \
|
||||
"$ref" $rewritten $sha1 ||
|
||||
die "Could not rewrite $ref"
|
||||
;;
|
||||
esac
|
||||
*)
|
||||
# NEEDSWORK: possibly add -Werror, making this an error
|
||||
warn "WARNING: '$ref' was rewritten into multiple commits:"
|
||||
warn "$rewritten"
|
||||
warn "WARNING: Ref '$ref' points to the first one now."
|
||||
rewritten=$(echo "$rewritten" | head -n 1)
|
||||
git update-ref -m "filter-branch: rewrite to first" \
|
||||
"$ref" $rewritten $sha1 ||
|
||||
die "Could not rewrite $ref"
|
||||
;;
|
||||
esac
|
||||
git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1
|
||||
count=$(($count+1))
|
||||
done < "$tempdir"/heads
|
||||
|
||||
# TODO: This should possibly go, with the semantics that all positive given
|
||||
# refs are updated, and their original heads stored in refs/original/
|
||||
# Filter tags
|
||||
|
||||
if [ "$filter_tag_name" ]; then
|
||||
git for-each-ref --format='%(objectname) %(objecttype) %(refname)' refs/tags |
|
||||
@@ -277,12 +383,15 @@ if [ "$filter_tag_name" ]; then
|
||||
warn "unreferencing tag object $sha1t"
|
||||
fi
|
||||
|
||||
git update-ref "refs/tags/$new_ref" "$new_sha1"
|
||||
git update-ref "refs/tags/$new_ref" "$new_sha1" ||
|
||||
die "Could not write tag $new_ref"
|
||||
done
|
||||
fi
|
||||
|
||||
cd ../..
|
||||
rm -rf "$tempdir"
|
||||
printf "\nRewritten history saved to the $dstbranch branch\n"
|
||||
echo
|
||||
test $count -gt 0 && echo "These refs were rewritten:"
|
||||
git show-ref | grep ^"$orig_namespace"
|
||||
|
||||
exit $ret
|
||||
|
||||
@@ -154,12 +154,10 @@ proc gitexec {args} {
|
||||
}
|
||||
|
||||
proc reponame {} {
|
||||
global _reponame
|
||||
return $_reponame
|
||||
return $::_reponame
|
||||
}
|
||||
|
||||
proc is_MacOSX {} {
|
||||
global tcl_platform tk_library
|
||||
if {[tk windowingsystem] eq {aqua}} {
|
||||
return 1
|
||||
}
|
||||
@@ -167,17 +165,16 @@ proc is_MacOSX {} {
|
||||
}
|
||||
|
||||
proc is_Windows {} {
|
||||
global tcl_platform
|
||||
if {$tcl_platform(platform) eq {windows}} {
|
||||
if {$::tcl_platform(platform) eq {windows}} {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
proc is_Cygwin {} {
|
||||
global tcl_platform _iscygwin
|
||||
global _iscygwin
|
||||
if {$_iscygwin eq {}} {
|
||||
if {$tcl_platform(platform) eq {windows}} {
|
||||
if {$::tcl_platform(platform) eq {windows}} {
|
||||
if {[catch {set p [exec cygpath --windir]} err]} {
|
||||
set _iscygwin 0
|
||||
} else {
|
||||
@@ -367,16 +364,25 @@ proc _which {what} {
|
||||
return {}
|
||||
}
|
||||
|
||||
proc _lappend_nice {cmd_var} {
|
||||
global _nice
|
||||
upvar $cmd_var cmd
|
||||
|
||||
if {![info exists _nice]} {
|
||||
set _nice [_which nice]
|
||||
}
|
||||
if {$_nice ne {}} {
|
||||
lappend cmd $_nice
|
||||
}
|
||||
}
|
||||
|
||||
proc git {args} {
|
||||
set opt [list exec]
|
||||
|
||||
while {1} {
|
||||
switch -- [lindex $args 0] {
|
||||
--nice {
|
||||
global _nice
|
||||
if {$_nice ne {}} {
|
||||
lappend opt $_nice
|
||||
}
|
||||
_lappend_nice opt
|
||||
}
|
||||
|
||||
default {
|
||||
@@ -414,6 +420,7 @@ proc _open_stdout_stderr {cmd} {
|
||||
error $err
|
||||
}
|
||||
}
|
||||
fconfigure $fd -eofchar {}
|
||||
return $fd
|
||||
}
|
||||
|
||||
@@ -423,10 +430,7 @@ proc git_read {args} {
|
||||
while {1} {
|
||||
switch -- [lindex $args 0] {
|
||||
--nice {
|
||||
global _nice
|
||||
if {$_nice ne {}} {
|
||||
lappend opt $_nice
|
||||
}
|
||||
_lappend_nice opt
|
||||
}
|
||||
|
||||
--stderr {
|
||||
@@ -454,10 +458,7 @@ proc git_write {args} {
|
||||
while {1} {
|
||||
switch -- [lindex $args 0] {
|
||||
--nice {
|
||||
global _nice
|
||||
if {$_nice ne {}} {
|
||||
lappend opt $_nice
|
||||
}
|
||||
_lappend_nice opt
|
||||
}
|
||||
|
||||
default {
|
||||
@@ -524,7 +525,6 @@ if {$_git eq {}} {
|
||||
error_popup "Cannot find git in PATH."
|
||||
exit 1
|
||||
}
|
||||
set _nice [_which nice]
|
||||
|
||||
######################################################################
|
||||
##
|
||||
@@ -544,8 +544,34 @@ if {![regsub {^git version } $_git_version {} _git_version]} {
|
||||
error_popup "Cannot parse Git version string:\n\n$_git_version"
|
||||
exit 1
|
||||
}
|
||||
|
||||
set _real_git_version $_git_version
|
||||
regsub -- {-dirty$} $_git_version {} _git_version
|
||||
regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
|
||||
regsub {\.rc[0-9]+$} $_git_version {} _git_version
|
||||
regsub {\.GIT$} $_git_version {} _git_version
|
||||
|
||||
if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
|
||||
catch {wm withdraw .}
|
||||
if {[tk_messageBox \
|
||||
-icon warning \
|
||||
-type yesno \
|
||||
-default no \
|
||||
-title "[appname]: warning" \
|
||||
-message "Git version cannot be determined.
|
||||
|
||||
$_git claims it is version '$_real_git_version'.
|
||||
|
||||
[appname] requires at least Git 1.5.0 or later.
|
||||
|
||||
Assume '$_real_git_version' is version 1.5.0?
|
||||
"] eq {yes}} {
|
||||
set _git_version 1.5.0
|
||||
} else {
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
unset _real_git_version
|
||||
|
||||
proc git-version {args} {
|
||||
global _git_version
|
||||
@@ -601,6 +627,46 @@ You are using [git-version]:
|
||||
exit 1
|
||||
}
|
||||
|
||||
######################################################################
|
||||
##
|
||||
## feature option selection
|
||||
|
||||
if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
|
||||
unset _junk
|
||||
} else {
|
||||
set subcommand gui
|
||||
}
|
||||
if {$subcommand eq {gui.sh}} {
|
||||
set subcommand gui
|
||||
}
|
||||
if {$subcommand eq {gui} && [llength $argv] > 0} {
|
||||
set subcommand [lindex $argv 0]
|
||||
set argv [lrange $argv 1 end]
|
||||
}
|
||||
|
||||
enable_option multicommit
|
||||
enable_option branch
|
||||
enable_option transport
|
||||
disable_option bare
|
||||
|
||||
switch -- $subcommand {
|
||||
browser -
|
||||
blame {
|
||||
enable_option bare
|
||||
|
||||
disable_option multicommit
|
||||
disable_option branch
|
||||
disable_option transport
|
||||
}
|
||||
citool {
|
||||
enable_option singlecommit
|
||||
|
||||
disable_option multicommit
|
||||
disable_option branch
|
||||
disable_option transport
|
||||
}
|
||||
}
|
||||
|
||||
######################################################################
|
||||
##
|
||||
## repository setup
|
||||
@@ -625,19 +691,24 @@ if {![file isdirectory $_gitdir]} {
|
||||
error_popup "Git directory not found:\n\n$_gitdir"
|
||||
exit 1
|
||||
}
|
||||
if {[lindex [file split $_gitdir] end] ne {.git}} {
|
||||
catch {wm withdraw .}
|
||||
error_popup "Cannot use funny .git directory:\n\n$_gitdir"
|
||||
exit 1
|
||||
if {![is_enabled bare]} {
|
||||
if {[lindex [file split $_gitdir] end] ne {.git}} {
|
||||
catch {wm withdraw .}
|
||||
error_popup "Cannot use funny .git directory:\n\n$_gitdir"
|
||||
exit 1
|
||||
}
|
||||
if {[catch {cd [file dirname $_gitdir]} err]} {
|
||||
catch {wm withdraw .}
|
||||
error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
if {[catch {cd [file dirname $_gitdir]} err]} {
|
||||
catch {wm withdraw .}
|
||||
error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
|
||||
exit 1
|
||||
set _reponame [file split [file normalize $_gitdir]]
|
||||
if {[lindex $_reponame end] eq {.git}} {
|
||||
set _reponame [lindex $_reponame end-1]
|
||||
} else {
|
||||
set _reponame [lindex $_reponame end]
|
||||
}
|
||||
set _reponame [lindex [file split \
|
||||
[file normalize [file dirname $_gitdir]]] \
|
||||
end]
|
||||
|
||||
######################################################################
|
||||
##
|
||||
@@ -758,8 +829,9 @@ proc rescan {after {honor_trustmtime 1}} {
|
||||
|
||||
array unset file_states
|
||||
|
||||
if {![$ui_comm edit modified]
|
||||
|| [string trim [$ui_comm get 0.0 end]] eq {}} {
|
||||
if {!$::GITGUI_BCK_exists &&
|
||||
(![$ui_comm edit modified]
|
||||
|| [string trim [$ui_comm get 0.0 end]] eq {})} {
|
||||
if {[string match amend* $commit_type]} {
|
||||
} elseif {[load_message GITGUI_MSG]} {
|
||||
} elseif {[load_message MERGE_MSG]} {
|
||||
@@ -800,6 +872,10 @@ proc rescan_stage2 {fd after} {
|
||||
if {[file readable $info_exclude]} {
|
||||
lappend ls_others "--exclude-from=$info_exclude"
|
||||
}
|
||||
set user_exclude [get_config core.excludesfile]
|
||||
if {$user_exclude ne {} && [file readable $user_exclude]} {
|
||||
lappend ls_others "--exclude-from=$user_exclude"
|
||||
}
|
||||
|
||||
set buf_rdi {}
|
||||
set buf_rdf {}
|
||||
@@ -827,6 +903,7 @@ proc load_message {file} {
|
||||
if {[catch {set fd [open $f r]}]} {
|
||||
return 0
|
||||
}
|
||||
fconfigure $fd -eofchar {}
|
||||
set content [string trim [read $fd]]
|
||||
close $fd
|
||||
regsub -all -line {[ \r\t]+$} $content {} content
|
||||
@@ -1219,32 +1296,6 @@ static unsigned char file_merge_bits[] = {
|
||||
0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
|
||||
} -maskdata $filemask
|
||||
|
||||
set file_dir_data {
|
||||
#define file_width 18
|
||||
#define file_height 18
|
||||
static unsigned char file_bits[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
|
||||
0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
|
||||
0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
|
||||
0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
}
|
||||
image create bitmap file_dir -background white -foreground blue \
|
||||
-data $file_dir_data -maskdata $file_dir_data
|
||||
unset file_dir_data
|
||||
|
||||
set file_uplevel_data {
|
||||
#define up_width 15
|
||||
#define up_height 15
|
||||
static unsigned char up_bits[] = {
|
||||
0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
|
||||
0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
|
||||
0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
|
||||
}
|
||||
image create bitmap file_uplevel -background white -foreground red \
|
||||
-data $file_uplevel_data -maskdata $file_uplevel_data
|
||||
unset file_uplevel_data
|
||||
|
||||
set ui_index .vpane.files.index.list
|
||||
set ui_workdir .vpane.files.workdir.list
|
||||
|
||||
@@ -1345,6 +1396,7 @@ set is_quitting 0
|
||||
|
||||
proc do_quit {} {
|
||||
global ui_comm is_quitting repo_config commit_type
|
||||
global GITGUI_BCK_exists GITGUI_BCK_i
|
||||
|
||||
if {$is_quitting} return
|
||||
set is_quitting 1
|
||||
@@ -1353,18 +1405,30 @@ proc do_quit {} {
|
||||
# -- Stash our current commit buffer.
|
||||
#
|
||||
set save [gitdir GITGUI_MSG]
|
||||
set msg [string trim [$ui_comm get 0.0 end]]
|
||||
regsub -all -line {[ \r\t]+$} $msg {} msg
|
||||
if {(![string match amend* $commit_type]
|
||||
|| [$ui_comm edit modified])
|
||||
&& $msg ne {}} {
|
||||
catch {
|
||||
set fd [open $save w]
|
||||
puts -nonewline $fd $msg
|
||||
close $fd
|
||||
}
|
||||
if {$GITGUI_BCK_exists && ![$ui_comm edit modified]} {
|
||||
file rename -force [gitdir GITGUI_BCK] $save
|
||||
set GITGUI_BCK_exists 0
|
||||
} else {
|
||||
catch {file delete $save}
|
||||
set msg [string trim [$ui_comm get 0.0 end]]
|
||||
regsub -all -line {[ \r\t]+$} $msg {} msg
|
||||
if {(![string match amend* $commit_type]
|
||||
|| [$ui_comm edit modified])
|
||||
&& $msg ne {}} {
|
||||
catch {
|
||||
set fd [open $save w]
|
||||
puts -nonewline $fd $msg
|
||||
close $fd
|
||||
}
|
||||
} else {
|
||||
catch {file delete $save}
|
||||
}
|
||||
}
|
||||
|
||||
# -- Remove our editor backup, its not needed.
|
||||
#
|
||||
after cancel $GITGUI_BCK_i
|
||||
if {$GITGUI_BCK_exists} {
|
||||
catch {file delete [gitdir GITGUI_BCK]}
|
||||
}
|
||||
|
||||
# -- Stash our current window geometry into this repository.
|
||||
@@ -1566,43 +1630,6 @@ set font_descs {
|
||||
load_config 0
|
||||
apply_config
|
||||
|
||||
######################################################################
|
||||
##
|
||||
## feature option selection
|
||||
|
||||
if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
|
||||
unset _junk
|
||||
} else {
|
||||
set subcommand gui
|
||||
}
|
||||
if {$subcommand eq {gui.sh}} {
|
||||
set subcommand gui
|
||||
}
|
||||
if {$subcommand eq {gui} && [llength $argv] > 0} {
|
||||
set subcommand [lindex $argv 0]
|
||||
set argv [lrange $argv 1 end]
|
||||
}
|
||||
|
||||
enable_option multicommit
|
||||
enable_option branch
|
||||
enable_option transport
|
||||
|
||||
switch -- $subcommand {
|
||||
browser -
|
||||
blame {
|
||||
disable_option multicommit
|
||||
disable_option branch
|
||||
disable_option transport
|
||||
}
|
||||
citool {
|
||||
enable_option singlecommit
|
||||
|
||||
disable_option multicommit
|
||||
disable_option branch
|
||||
disable_option transport
|
||||
}
|
||||
}
|
||||
|
||||
######################################################################
|
||||
##
|
||||
## ui construction
|
||||
@@ -1632,20 +1659,32 @@ if {[is_enabled transport]} {
|
||||
menu .mbar.repository
|
||||
|
||||
.mbar.repository add command \
|
||||
-label {Browse Current Branch} \
|
||||
-label {Browse Current Branch's Files} \
|
||||
-command {browser::new $current_branch}
|
||||
trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
|
||||
set ui_browse_current [.mbar.repository index last]
|
||||
.mbar.repository add command \
|
||||
-label {Browse Branch Files...} \
|
||||
-command browser_open::dialog
|
||||
.mbar.repository add separator
|
||||
|
||||
.mbar.repository add command \
|
||||
-label {Visualize Current Branch} \
|
||||
-label {Visualize Current Branch's History} \
|
||||
-command {do_gitk $current_branch}
|
||||
trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
|
||||
set ui_visualize_current [.mbar.repository index last]
|
||||
.mbar.repository add command \
|
||||
-label {Visualize All Branches} \
|
||||
-label {Visualize All Branch History} \
|
||||
-command {do_gitk --all}
|
||||
.mbar.repository add separator
|
||||
|
||||
proc current_branch_write {args} {
|
||||
global current_branch
|
||||
.mbar.repository entryconf $::ui_browse_current \
|
||||
-label "Browse $current_branch's Files"
|
||||
.mbar.repository entryconf $::ui_visualize_current \
|
||||
-label "Visualize $current_branch's History"
|
||||
}
|
||||
trace add variable current_branch write current_branch_write
|
||||
|
||||
if {[is_enabled multicommit]} {
|
||||
.mbar.repository add command -label {Database Statistics} \
|
||||
-command do_stats
|
||||
@@ -1766,12 +1805,12 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
|
||||
lappend disable_on_lock \
|
||||
[list .mbar.commit entryconf [.mbar.commit index last] -state]
|
||||
|
||||
.mbar.commit add command -label {Add To Commit} \
|
||||
.mbar.commit add command -label {Stage To Commit} \
|
||||
-command do_add_selection
|
||||
lappend disable_on_lock \
|
||||
[list .mbar.commit entryconf [.mbar.commit index last] -state]
|
||||
|
||||
.mbar.commit add command -label {Add Existing To Commit} \
|
||||
.mbar.commit add command -label {Stage Changed Files To Commit} \
|
||||
-command do_add_all \
|
||||
-accelerator $M1T-I
|
||||
lappend disable_on_lock \
|
||||
@@ -1805,14 +1844,14 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
|
||||
if {[is_enabled branch]} {
|
||||
menu .mbar.merge
|
||||
.mbar.merge add command -label {Local Merge...} \
|
||||
-command merge::dialog
|
||||
-command merge::dialog \
|
||||
-accelerator $M1T-M
|
||||
lappend disable_on_lock \
|
||||
[list .mbar.merge entryconf [.mbar.merge index last] -state]
|
||||
.mbar.merge add command -label {Abort Merge...} \
|
||||
-command merge::reset_hard
|
||||
lappend disable_on_lock \
|
||||
[list .mbar.merge entryconf [.mbar.merge index last] -state]
|
||||
|
||||
}
|
||||
|
||||
# -- Transport Menu
|
||||
@@ -1844,33 +1883,6 @@ if {[is_MacOSX]} {
|
||||
.mbar.edit add separator
|
||||
.mbar.edit add command -label {Options...} \
|
||||
-command do_options
|
||||
|
||||
# -- Tools Menu
|
||||
#
|
||||
if {[is_Cygwin] && [file exists /usr/local/miga/lib/gui-miga]} {
|
||||
proc do_miga {} {
|
||||
if {![lock_index update]} return
|
||||
set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""]
|
||||
set miga_fd [open "|$cmd" r]
|
||||
fconfigure $miga_fd -blocking 0
|
||||
fileevent $miga_fd readable [list miga_done $miga_fd]
|
||||
ui_status {Running miga...}
|
||||
}
|
||||
proc miga_done {fd} {
|
||||
read $fd 512
|
||||
if {[eof $fd]} {
|
||||
close $fd
|
||||
unlock_index
|
||||
rescan ui_ready
|
||||
}
|
||||
}
|
||||
.mbar add cascade -label Tools -menu .mbar.tools
|
||||
menu .mbar.tools
|
||||
.mbar.tools add command -label "Migrate" \
|
||||
-command do_miga
|
||||
lappend disable_on_lock \
|
||||
[list .mbar.tools entryconf [.mbar.tools index last] -state]
|
||||
}
|
||||
}
|
||||
|
||||
# -- Help Menu
|
||||
@@ -1938,29 +1950,10 @@ proc usage {} {
|
||||
# -- Not a normal commit type invocation? Do that instead!
|
||||
#
|
||||
switch -- $subcommand {
|
||||
browser {
|
||||
set subcommand_args {rev?}
|
||||
switch [llength $argv] {
|
||||
0 { load_current_branch }
|
||||
1 {
|
||||
set current_branch [lindex $argv 0]
|
||||
if {[regexp {^[0-9a-f]{1,39}$} $current_branch]} {
|
||||
if {[catch {
|
||||
set current_branch \
|
||||
[git rev-parse --verify $current_branch]
|
||||
} err]} {
|
||||
puts stderr $err
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
default usage
|
||||
}
|
||||
browser::new $current_branch
|
||||
return
|
||||
}
|
||||
browser -
|
||||
blame {
|
||||
set subcommand_args {rev? path?}
|
||||
set subcommand_args {rev? path}
|
||||
if {$argv eq {}} usage
|
||||
set head {}
|
||||
set path {}
|
||||
set is_path 0
|
||||
@@ -1979,12 +1972,18 @@ blame {
|
||||
} elseif {$head eq {}} {
|
||||
if {$head ne {}} usage
|
||||
set head $a
|
||||
set is_path 1
|
||||
} else {
|
||||
usage
|
||||
}
|
||||
}
|
||||
unset is_path
|
||||
|
||||
if {$head ne {} && $path eq {}} {
|
||||
set path $_prefix$head
|
||||
set head {}
|
||||
}
|
||||
|
||||
if {$head eq {}} {
|
||||
load_current_branch
|
||||
} else {
|
||||
@@ -1999,8 +1998,26 @@ blame {
|
||||
set current_branch $head
|
||||
}
|
||||
|
||||
if {$path eq {}} usage
|
||||
blame::new $head $path
|
||||
switch -- $subcommand {
|
||||
browser {
|
||||
if {$head eq {}} {
|
||||
if {$path ne {} && [file isdirectory $path]} {
|
||||
set head $current_branch
|
||||
} else {
|
||||
set head $path
|
||||
set path {}
|
||||
}
|
||||
}
|
||||
browser::new $head $path
|
||||
}
|
||||
blame {
|
||||
if {$head eq {} && ![file exists $path]} {
|
||||
puts stderr "fatal: cannot stat path $path: No such file or directory"
|
||||
exit 1
|
||||
}
|
||||
blame::new $head $path
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
citool -
|
||||
@@ -2115,7 +2132,7 @@ pack .vpane.lower.commarea.buttons.rescan -side top -fill x
|
||||
lappend disable_on_lock \
|
||||
{.vpane.lower.commarea.buttons.rescan conf -state}
|
||||
|
||||
button .vpane.lower.commarea.buttons.incall -text {Add Existing} \
|
||||
button .vpane.lower.commarea.buttons.incall -text {Stage Changed} \
|
||||
-command do_add_all
|
||||
pack .vpane.lower.commarea.buttons.incall -side top -fill x
|
||||
lappend disable_on_lock \
|
||||
@@ -2389,17 +2406,25 @@ lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
|
||||
$ctxm add separator
|
||||
$ctxm add command -label {Options...} \
|
||||
-command do_options
|
||||
bind_button3 $ui_diff "
|
||||
set cursorX %x
|
||||
set cursorY %y
|
||||
if {\$ui_index eq \$current_diff_side} {
|
||||
$ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit}
|
||||
proc popup_diff_menu {ctxm x y X Y} {
|
||||
set ::cursorX $x
|
||||
set ::cursorY $y
|
||||
if {$::ui_index eq $::current_diff_side} {
|
||||
$ctxm entryconf $::ui_diff_applyhunk \
|
||||
-state normal \
|
||||
-label {Unstage Hunk From Commit}
|
||||
} elseif {{_O} eq [lindex $::file_states($::current_diff_path) 0]} {
|
||||
$ctxm entryconf $::ui_diff_applyhunk \
|
||||
-state disabled \
|
||||
-label {Stage Hunk For Commit}
|
||||
} else {
|
||||
$ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit}
|
||||
$ctxm entryconf $::ui_diff_applyhunk \
|
||||
-state normal \
|
||||
-label {Stage Hunk For Commit}
|
||||
}
|
||||
tk_popup $ctxm %X %Y
|
||||
"
|
||||
unset ui_diff_applyhunk
|
||||
tk_popup $ctxm $X $Y
|
||||
}
|
||||
bind_button3 $ui_diff [list popup_diff_menu $ctxm %x %y %X %Y]
|
||||
|
||||
# -- Status Bar
|
||||
#
|
||||
@@ -2460,6 +2485,8 @@ if {[is_enabled branch]} {
|
||||
bind . <$M1B-Key-N> branch_create::dialog
|
||||
bind . <$M1B-Key-o> branch_checkout::dialog
|
||||
bind . <$M1B-Key-O> branch_checkout::dialog
|
||||
bind . <$M1B-Key-m> merge::dialog
|
||||
bind . <$M1B-Key-M> merge::dialog
|
||||
}
|
||||
if {[is_enabled transport]} {
|
||||
bind . <$M1B-Key-p> do_push_anywhere
|
||||
@@ -2551,24 +2578,64 @@ if {[is_enabled transport]} {
|
||||
populate_push_menu
|
||||
}
|
||||
|
||||
# -- Only suggest a gc run if we are going to stay running.
|
||||
#
|
||||
if {[is_enabled multicommit]} {
|
||||
set object_limit 2000
|
||||
if {[is_Windows]} {set object_limit 200}
|
||||
regexp {^([0-9]+) objects,} [git count-objects] _junk objects_current
|
||||
if {$objects_current >= $object_limit} {
|
||||
if {[ask_popup \
|
||||
"This repository currently has $objects_current loose objects.
|
||||
if {[winfo exists $ui_comm]} {
|
||||
set GITGUI_BCK_exists [load_message GITGUI_BCK]
|
||||
|
||||
To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
|
||||
|
||||
Compress the database now?"] eq yes} {
|
||||
do_gc
|
||||
# -- If both our backup and message files exist use the
|
||||
# newer of the two files to initialize the buffer.
|
||||
#
|
||||
if {$GITGUI_BCK_exists} {
|
||||
set m [gitdir GITGUI_MSG]
|
||||
if {[file isfile $m]} {
|
||||
if {[file mtime [gitdir GITGUI_BCK]] > [file mtime $m]} {
|
||||
catch {file delete [gitdir GITGUI_MSG]}
|
||||
} else {
|
||||
$ui_comm delete 0.0 end
|
||||
$ui_comm edit reset
|
||||
$ui_comm edit modified false
|
||||
catch {file delete [gitdir GITGUI_BCK]}
|
||||
set GITGUI_BCK_exists 0
|
||||
}
|
||||
}
|
||||
unset m
|
||||
}
|
||||
unset object_limit _junk objects_current
|
||||
|
||||
proc backup_commit_buffer {} {
|
||||
global ui_comm GITGUI_BCK_exists
|
||||
|
||||
set m [$ui_comm edit modified]
|
||||
if {$m || $GITGUI_BCK_exists} {
|
||||
set msg [string trim [$ui_comm get 0.0 end]]
|
||||
regsub -all -line {[ \r\t]+$} $msg {} msg
|
||||
|
||||
if {$msg eq {}} {
|
||||
if {$GITGUI_BCK_exists} {
|
||||
catch {file delete [gitdir GITGUI_BCK]}
|
||||
set GITGUI_BCK_exists 0
|
||||
}
|
||||
} elseif {$m} {
|
||||
catch {
|
||||
set fd [open [gitdir GITGUI_BCK] w]
|
||||
puts -nonewline $fd $msg
|
||||
close $fd
|
||||
set GITGUI_BCK_exists 1
|
||||
}
|
||||
}
|
||||
|
||||
$ui_comm edit modified false
|
||||
}
|
||||
|
||||
set ::GITGUI_BCK_i [after 2000 backup_commit_buffer]
|
||||
}
|
||||
|
||||
backup_commit_buffer
|
||||
}
|
||||
|
||||
lock_index begin-read
|
||||
if {![winfo ismapped .]} {
|
||||
wm deiconify .
|
||||
}
|
||||
after 1 do_rescan
|
||||
if {[is_enabled multicommit]} {
|
||||
after 1000 hint_gc
|
||||
}
|
||||
|
||||
@@ -370,6 +370,7 @@ method _load {jump} {
|
||||
$w_path conf -text [escape_path $path]
|
||||
if {$commit eq {}} {
|
||||
set fd [open $path r]
|
||||
fconfigure $fd -eofchar {}
|
||||
} else {
|
||||
set fd [git_read cat-file blob "$commit:$path"]
|
||||
}
|
||||
@@ -770,15 +771,20 @@ method _showcommit {cur_w lno} {
|
||||
set enc [string tolower [string range $line 9 end]]
|
||||
}
|
||||
}
|
||||
set msg [encoding convertfrom $enc [read $fd]]
|
||||
set msg [string trim $msg]
|
||||
set msg [read $fd]
|
||||
close $fd
|
||||
|
||||
set author_name [encoding convertfrom $enc $author_name]
|
||||
set committer_name [encoding convertfrom $enc $committer_name]
|
||||
|
||||
set header($cmit,author) $author_name
|
||||
set header($cmit,committer) $committer_name
|
||||
set enc [tcl_encoding $enc]
|
||||
if {$enc ne {}} {
|
||||
set msg [encoding convertfrom $enc $msg]
|
||||
set author_name [encoding convertfrom $enc $author_name]
|
||||
set committer_name [encoding convertfrom $enc $committer_name]
|
||||
set header($cmit,author) $author_name
|
||||
set header($cmit,committer) $committer_name
|
||||
set header($cmit,summary) \
|
||||
[encoding convertfrom $enc $header($cmit,summary)]
|
||||
}
|
||||
set msg [string trim $msg]
|
||||
}
|
||||
set header($cmit,message) $msg
|
||||
}
|
||||
@@ -873,6 +879,11 @@ method _open_tooltip {cur_w} {
|
||||
set org [lindex $amov_data $lno]
|
||||
}
|
||||
|
||||
if {$dat eq {}} {
|
||||
_hide_tooltip $this
|
||||
return
|
||||
}
|
||||
|
||||
set cmit [lindex $dat 0]
|
||||
set tooltip_commit [list $cmit]
|
||||
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
|
||||
class browser {
|
||||
|
||||
image create photo ::browser::img_parent -data {R0lGODlhEAAQAIUAAPwCBBxSHBxOHMTSzNzu3KzCtBRGHCSKFIzCjLzSxBQ2FAxGHDzCLCyeHBQ+FHSmfAwuFBxKLDSCNMzizISyjJzOnDSyLAw+FAQSDAQeDBxWJAwmDAQOBKzWrDymNAQaDAQODAwaDDyKTFSyXFTGTEy6TAQCBAQKDAwiFBQyHAwSFAwmHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAZ1QIBwSCwaj0hiQCBICpcDQsFgGAaIguhhi0gohIsrQEDYMhiNrRfgeAQC5fMCAolIDhD2hFI5WC4YRBkaBxsOE2l/RxsHHA4dHmkfRyAbIQ4iIyQlB5NFGCAACiakpSZEJyinTgAcKSesACorgU4mJ6uxR35BACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs=}
|
||||
image create photo ::browser::img_rblob -data {R0lGODlhEAAQAIUAAPwCBFxaXNze3Ly2rJSWjPz+/Ozq7GxqbJyanPT29HRydMzOzDQyNIyKjERCROTi3Pz69PTy7Pzy7PTu5Ozm3LyqlJyWlJSSjJSOhOzi1LyulPz27PTq3PTm1OzezLyqjIyKhJSKfOzaxPz29OzizLyidIyGdIyCdOTOpLymhOzavOTStMTCtMS+rMS6pMSynMSulLyedAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAaQQIAQECgajcNkQMBkDgKEQFK4LFgLhkMBIVUKroWEYlEgMLxbBKLQUBwc52HgAQ4LBo049atWQyIPA3pEdFcQEhMUFYNVagQWFxgZGoxfYRsTHB0eH5UJCJAYICEinUoPIxIcHCQkIiIllQYEGCEhJicoKYwPmiQeKisrKLFKLCwtLi8wHyUlMYwM0tPUDH5BACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs=}
|
||||
image create photo ::browser::img_xblob -data {R0lGODlhEAAQAIYAAPwCBFRWVFxaXNza3OTi3Nze3Ly2tJyanPz+/Ozq7GxubNzSxMzOzMTGxHRybDQyNLy+vHRydHx6fKSipISChIyKjGxqbERCRCwuLLy6vGRiZExKTCQiJAwKDLSytLy2rJSSlHx+fDw6PKyqrBQWFPTu5Ozm3LyulLS2tCQmJAQCBPTq3Ozi1MSynCwqLAQGBOTazOzizOzezLyqjBweHNzSvOzaxKyurHRuZNzOtLymhDw+PIyCdOzWvOTOpLyidNzKtOTStLyifMTCtMS+rLyedAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAfZgACCAAEChYeGg4oCAwQFjgYBBwGKggEECJkICQoIkwADCwwNDY2mDA4Lng8QDhESsLARExQVDhYXGBkWExIaGw8cHR4SCQQfFQ8eFgUgIQEiwiMSBMYfGB4atwEXDyQd0wQlJicPKAHoFyIpJCoeDgMrLC0YKBsX6i4kL+4OMDEyZijr5oLGNxUqUCioEcPGDAwjPNyI6MEDChQjcOSwsUDHgw07RIgI4KCkAgs8cvTw8eOBogAxQtXIASTISiEuBwUYMoRIixYnZggpUgTDywdIkWJIitRPIAAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7}
|
||||
image create photo ::browser::img_tree -data {R0lGODlhEAAQAIYAAPwCBAQCBExKTBwWHMzKzOzq7ERCRExGTCwqLARqnAQ+ZHR2dKyqrNTOzHx2fCQiJMTi9NTu9HzC3AxmnAQ+XPTm7Dy67DymzITC3IzG5AxypHRydKymrMzOzOzu7BweHByy9AyGtFyy1IzG3NTu/ARupFRSVByazBR6rAyGvFyuzJTK3MTm9BR+tAxWhHS61MTi7Pz+/IymvCxulBRelAx2rHS63Pz6/PTy9PTu9Nza3ISitBRupFSixNTS1CxqnDQyNMzGzOTi5MTCxMTGxGxubGxqbLy2vLSutGRiZLy6vLSytKyurDQuNFxaXKSipDw6PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAfDgACCAAECg4eIAAMEBQYHCImDBgkKCwwNBQIBBw4Bhw8QERITFJYEFQUFnoIPFhcYoRkaFBscHR4Ggh8gIRciEiMQJBkltCa6JyUoKSkXKhIrLCQYuQAPLS4TEyUhKb0qLzDVAjEFMjMuNBMoNcw21QY3ODkFOjs82RM1PfDzFRU3fOggcM7Fj2pAgggRokOHDx9DhhAZUqQaISBGhjwMEvEIkiIHEgUAkgSJkiNLmFSMJChAEydPGBSBwvJQgAc0/QQCACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs=}
|
||||
image create photo ::browser::img_symlink -data {R0lGODlhEAAQAIQAAPwCBCwqLLSytLy+vERGRFRWVDQ2NKSmpAQCBKyurMTGxISChJyanHR2dIyKjGxubHRydGRmZIyOjFxeXHx6fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAVbICACwWieY1CibCCsrBkMb0zchSEcNYskCtqBBzshFkOGQFk0IRqOxqPBODRHCMhCQKteRc9FI/KQWGOIyFYgkDC+gPR4snCcfRGKOIKIgSMQE31+f4OEYCZ+IQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7}
|
||||
image create photo ::browser::img_unknown -data {R0lGODlhEAAQAIUAAPwCBFxaXIyKjNTW1Nze3LS2tJyanER2RGS+VPz+/PTu5GxqbPz69BQ6BCxeLFSqRPT29HRydMzOzDQyNERmPKSypCRWHIyKhERCRDyGPKz2nESiLBxGHCyCHGxubPz6/PTy7Ozi1Ly2rKSipOzm3LyqlKSWhCRyFOzizLymhNTKtNzOvOzaxOTStPz27OzWvOTOpLSupLyedMS+rMS6pMSulLyqjLymfLyifAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAamQIAQECgajcOkYEBoDgoBQyAJOCCuiENCsWBIh9aGw9F4HCARiXciRDQoBUnlYRlcIgsMG5CxXAgMGhscBRAEBRd7AB0eBBoIgxUfICEiikSPgyMMIAokJZcBkBybJgomIaBJAZoMpyCmqkMBFCcVCrgKKAwpoSorKqchKCwtvasIFBIhLiYvLzDHsxQNMcMKLDAwMqEz3jQ1NTY3ONyrE+jp6hN+QQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7}
|
||||
|
||||
field w
|
||||
field browser_commit
|
||||
field browser_path
|
||||
@@ -13,13 +20,13 @@ field browser_busy 1
|
||||
|
||||
field ls_buf {}; # Buffered record output from ls-tree
|
||||
|
||||
constructor new {commit} {
|
||||
constructor new {commit {path {}}} {
|
||||
global cursor_ptr M1B
|
||||
make_toplevel top w
|
||||
wm title $top "[appname] ([reponame]): File Browser"
|
||||
|
||||
set browser_commit $commit
|
||||
set browser_path $browser_commit:
|
||||
set browser_path $browser_commit:$path
|
||||
|
||||
label $w.path \
|
||||
-textvariable @browser_path \
|
||||
@@ -73,7 +80,11 @@ constructor new {commit} {
|
||||
|
||||
bind $w_list <Visibility> [list focus $w_list]
|
||||
set w $w_list
|
||||
_ls $this $browser_commit
|
||||
if {$path ne {}} {
|
||||
_ls $this $browser_commit:$path $path
|
||||
} else {
|
||||
_ls $this $browser_commit $path
|
||||
}
|
||||
return $this
|
||||
}
|
||||
|
||||
@@ -173,7 +184,7 @@ method _ls {tree_id {name {}}} {
|
||||
$w image create end \
|
||||
-align center -padx 5 -pady 1 \
|
||||
-name icon0 \
|
||||
-image file_uplevel
|
||||
-image ::browser::img_parent
|
||||
$w insert end {[Up To Parent]}
|
||||
lappend browser_files parent
|
||||
}
|
||||
@@ -203,14 +214,21 @@ method _read {fd} {
|
||||
|
||||
switch -- $type {
|
||||
blob {
|
||||
set image file_mod
|
||||
scan [lindex $info 0] %o mode
|
||||
if {$mode == 0120000} {
|
||||
set image ::browser::img_symlink
|
||||
} elseif {($mode & 0100) != 0} {
|
||||
set image ::browser::img_xblob
|
||||
} else {
|
||||
set image ::browser::img_rblob
|
||||
}
|
||||
}
|
||||
tree {
|
||||
set image file_dir
|
||||
set image ::browser::img_tree
|
||||
append path /
|
||||
}
|
||||
default {
|
||||
set image file_question
|
||||
set image ::browser::img_unknown
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,3 +257,56 @@ method _read {fd} {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class browser_open {
|
||||
|
||||
field w ; # widget path
|
||||
field w_rev ; # mega-widget to pick the initial revision
|
||||
|
||||
constructor dialog {} {
|
||||
make_toplevel top w
|
||||
wm title $top "[appname] ([reponame]): Browse Branch Files"
|
||||
if {$top ne {.}} {
|
||||
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
|
||||
}
|
||||
|
||||
label $w.header \
|
||||
-text {Browse Branch Files} \
|
||||
-font font_uibold
|
||||
pack $w.header -side top -fill x
|
||||
|
||||
frame $w.buttons
|
||||
button $w.buttons.browse -text Browse \
|
||||
-default active \
|
||||
-command [cb _open]
|
||||
pack $w.buttons.browse -side right
|
||||
button $w.buttons.cancel -text {Cancel} \
|
||||
-command [list destroy $w]
|
||||
pack $w.buttons.cancel -side right -padx 5
|
||||
pack $w.buttons -side bottom -fill x -pady 10 -padx 10
|
||||
|
||||
set w_rev [::choose_rev::new $w.rev {Revision}]
|
||||
$w_rev bind_listbox <Double-Button-1> [cb _open]
|
||||
pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
|
||||
|
||||
bind $w <Visibility> [cb _visible]
|
||||
bind $w <Key-Escape> [list destroy $w]
|
||||
bind $w <Key-Return> [cb _open]\;break
|
||||
tkwait window $w
|
||||
}
|
||||
|
||||
method _open {} {
|
||||
if {[catch {$w_rev commit_or_die} err]} {
|
||||
return
|
||||
}
|
||||
set name [$w_rev get]
|
||||
destroy $w
|
||||
browser::new $name
|
||||
}
|
||||
|
||||
method _visible {} {
|
||||
grab $w
|
||||
$w_rev focus_filter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ field new_ref ; # ref we are updating/creating
|
||||
|
||||
field parent_w .; # window that started us
|
||||
field merge_type none; # type of merge to apply to existing branch
|
||||
field merge_base {}; # merge base if we have another ref involved
|
||||
field fetch_spec {}; # refetch tracking branch if used?
|
||||
field checkout 1; # actually checkout the branch?
|
||||
field create 0; # create the branch if it doesn't exist?
|
||||
@@ -65,14 +66,19 @@ method run {} {
|
||||
set r_head [lindex $fetch_spec 2]
|
||||
regsub ^refs/heads/ $r_head {} r_name
|
||||
|
||||
set cmd [list git fetch $remote]
|
||||
if {$l_trck ne {}} {
|
||||
lappend cmd +$r_head:$l_trck
|
||||
} else {
|
||||
lappend cmd $r_head
|
||||
}
|
||||
|
||||
_toplevel $this {Refreshing Tracking Branch}
|
||||
set w_cons [::console::embed \
|
||||
$w.console \
|
||||
"Fetching $r_name from $remote"]
|
||||
pack $w.console -fill both -expand 1
|
||||
$w_cons exec \
|
||||
[list git fetch $remote +$r_head:$l_trck] \
|
||||
[cb _finish_fetch]
|
||||
$w_cons exec $cmd [cb _finish_fetch]
|
||||
|
||||
bind $w <$M1B-Key-w> break
|
||||
bind $w <$M1B-Key-W> break
|
||||
@@ -113,6 +119,9 @@ method _noop {} {}
|
||||
method _finish_fetch {ok} {
|
||||
if {$ok} {
|
||||
set l_trck [lindex $fetch_spec 0]
|
||||
if {$l_trck eq {}} {
|
||||
set l_trck FETCH_HEAD
|
||||
}
|
||||
if {[catch {set new_hash [git rev-parse --verify "$l_trck^0"]} err]} {
|
||||
set ok 0
|
||||
$w_cons insert "fatal: Cannot resolve $l_trck"
|
||||
@@ -180,29 +189,25 @@ method _update_ref {} {
|
||||
# No merge would be required, don't compute anything.
|
||||
#
|
||||
} else {
|
||||
set mrb {}
|
||||
catch {set mrb [git merge-base $new $cur]}
|
||||
switch -- $merge_type {
|
||||
ff {
|
||||
if {$mrb eq $new} {
|
||||
# The current branch is actually newer.
|
||||
#
|
||||
set new $cur
|
||||
} elseif {$mrb eq $cur} {
|
||||
# The current branch is older.
|
||||
#
|
||||
set reflog_msg "merge $new_expr: Fast-forward"
|
||||
} else {
|
||||
_error $this "Branch '$newbranch' already exists.\n\nIt cannot fast-forward to $new_expr.\nA merge is required."
|
||||
return 0
|
||||
catch {set merge_base [git merge-base $new $cur]}
|
||||
if {$merge_base eq $cur} {
|
||||
# The current branch is older.
|
||||
#
|
||||
set reflog_msg "merge $new_expr: Fast-forward"
|
||||
} else {
|
||||
switch -- $merge_type {
|
||||
ff {
|
||||
if {$merge_base eq $new} {
|
||||
# The current branch is actually newer.
|
||||
#
|
||||
set new $cur
|
||||
set new_hash $cur
|
||||
} else {
|
||||
_error $this "Branch '$newbranch' already exists.\n\nIt cannot fast-forward to $new_expr.\nA merge is required."
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
reset {
|
||||
if {$mrb eq $cur} {
|
||||
# The current branch is older.
|
||||
#
|
||||
set reflog_msg "merge $new_expr: Fast-forward"
|
||||
} else {
|
||||
reset {
|
||||
# The current branch will lose things.
|
||||
#
|
||||
if {[_confirm_reset $this $cur]} {
|
||||
@@ -211,11 +216,11 @@ method _update_ref {} {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
default {
|
||||
_error $this "Only 'ff' and 'reset' merge is currently supported."
|
||||
return 0
|
||||
}
|
||||
default {
|
||||
_error $this "Merge strategy '$merge_type' not supported."
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +248,7 @@ method _checkout {} {
|
||||
if {[lock_index checkout_op]} {
|
||||
after idle [cb _start_checkout]
|
||||
} else {
|
||||
_error $this "Index is already locked."
|
||||
_error $this "Staging area (index) is already locked."
|
||||
delete_this
|
||||
}
|
||||
}
|
||||
@@ -270,7 +275,9 @@ The rescan will be automatically started now.
|
||||
return
|
||||
}
|
||||
|
||||
if {[is_config_true gui.trustmtime]} {
|
||||
if {$curHEAD eq $new_hash} {
|
||||
_after_readtree $this
|
||||
} elseif {[is_config_true gui.trustmtime]} {
|
||||
_readtree $this
|
||||
} else {
|
||||
ui_status {Refreshing file status...}
|
||||
@@ -378,22 +385,24 @@ method _after_readtree {} {
|
||||
set rn [string length $rh]
|
||||
if {[string equal -length $rn $rh $new_ref]} {
|
||||
set new_branch [string range $new_ref $rn end]
|
||||
append log " to $new_branch"
|
||||
|
||||
if {[catch {
|
||||
git symbolic-ref -m $log HEAD $new_ref
|
||||
} err]} {
|
||||
_fatal $this $err
|
||||
if {$is_detached || $current_branch ne $new_branch} {
|
||||
append log " to $new_branch"
|
||||
if {[catch {
|
||||
git symbolic-ref -m $log HEAD $new_ref
|
||||
} err]} {
|
||||
_fatal $this $err
|
||||
}
|
||||
set current_branch $new_branch
|
||||
set is_detached 0
|
||||
}
|
||||
set current_branch $new_branch
|
||||
set is_detached 0
|
||||
} else {
|
||||
append log " to $new_expr"
|
||||
|
||||
if {[catch {
|
||||
_detach_HEAD $log $new_hash
|
||||
} err]} {
|
||||
_fatal $this $err
|
||||
if {$new_hash ne $HEAD} {
|
||||
append log " to $new_expr"
|
||||
if {[catch {
|
||||
_detach_HEAD $log $new_hash
|
||||
} err]} {
|
||||
_fatal $this $err
|
||||
}
|
||||
}
|
||||
set current_branch HEAD
|
||||
set is_detached 1
|
||||
|
||||
@@ -16,10 +16,28 @@ field cur_specs [list]; # list of specs for $revtype
|
||||
field spec_head ; # list of all head specs
|
||||
field spec_trck ; # list of all tracking branch specs
|
||||
field spec_tag ; # list of all tag specs
|
||||
field tip_data ; # array of tip commit info by refname
|
||||
field log_last ; # array of reflog date by refname
|
||||
|
||||
constructor new {path {title {}}} {
|
||||
field tooltip_wm {} ; # Current tooltip toplevel, if open
|
||||
field tooltip_t {} ; # Text widget in $tooltip_wm
|
||||
field tooltip_timer {} ; # Current timer event for our tooltip
|
||||
|
||||
proc new {path {title {}}} {
|
||||
return [_new $path 0 $title]
|
||||
}
|
||||
|
||||
proc new_unmerged {path {title {}}} {
|
||||
return [_new $path 1 $title]
|
||||
}
|
||||
|
||||
constructor _new {path unmerged_only title} {
|
||||
global current_branch is_detached
|
||||
|
||||
if {![info exists ::all_remotes]} {
|
||||
load_all_remotes
|
||||
}
|
||||
|
||||
set w $path
|
||||
|
||||
if {$title ne {}} {
|
||||
@@ -86,13 +104,17 @@ constructor new {path {title {}}} {
|
||||
listbox $w_list \
|
||||
-font font_diff \
|
||||
-width 50 \
|
||||
-height 5 \
|
||||
-height 10 \
|
||||
-selectmode browse \
|
||||
-exportselection false \
|
||||
-xscrollcommand [cb _sb_set $w.list.sbx h] \
|
||||
-yscrollcommand [cb _sb_set $w.list.sby v]
|
||||
pack $w_list -fill both -expand 1
|
||||
grid $w.list -sticky nswe -padx {20 5} -columnspan 2
|
||||
bind $w_list <Any-Motion> [cb _show_tooltip @%x,%y]
|
||||
bind $w_list <Any-Enter> [cb _hide_tooltip]
|
||||
bind $w_list <Any-Leave> [cb _hide_tooltip]
|
||||
bind $w_list <Destroy> [cb _hide_tooltip]
|
||||
|
||||
grid columnconfigure $w 1 -weight 1
|
||||
if {$is_detached} {
|
||||
@@ -105,21 +127,89 @@ constructor new {path {title {}}} {
|
||||
bind $w_filter <Key-Return> [list focus $w_list]\;break
|
||||
bind $w_filter <Key-Down> [list focus $w_list]
|
||||
|
||||
set fmt list
|
||||
append fmt { %(refname)}
|
||||
append fmt { [list}
|
||||
append fmt { %(objecttype)}
|
||||
append fmt { %(objectname)}
|
||||
append fmt { [concat %(taggername) %(authorname)]}
|
||||
append fmt { [concat %(taggerdate) %(authordate)]}
|
||||
append fmt { %(subject)}
|
||||
append fmt {] [list}
|
||||
append fmt { %(*objecttype)}
|
||||
append fmt { %(*objectname)}
|
||||
append fmt { %(*authorname)}
|
||||
append fmt { %(*authordate)}
|
||||
append fmt { %(*subject)}
|
||||
append fmt {]}
|
||||
set all_refn [list]
|
||||
set fr_fd [git_read for-each-ref \
|
||||
--tcl \
|
||||
--sort=-taggerdate \
|
||||
--format=$fmt \
|
||||
refs/heads \
|
||||
refs/remotes \
|
||||
refs/tags \
|
||||
]
|
||||
fconfigure $fr_fd -translation lf -encoding utf-8
|
||||
while {[gets $fr_fd line] > 0} {
|
||||
set line [eval $line]
|
||||
if {[lindex $line 1 0] eq {tag}} {
|
||||
if {[lindex $line 2 0] eq {commit}} {
|
||||
set sha1 [lindex $line 2 1]
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} elseif {[lindex $line 1 0] eq {commit}} {
|
||||
set sha1 [lindex $line 1 1]
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
set refn [lindex $line 0]
|
||||
set tip_data($refn) [lrange $line 1 end]
|
||||
lappend cmt_refn($sha1) $refn
|
||||
lappend all_refn $refn
|
||||
}
|
||||
close $fr_fd
|
||||
|
||||
if {$unmerged_only} {
|
||||
set fr_fd [git_read rev-list --all ^$::HEAD]
|
||||
while {[gets $fr_fd sha1] > 0} {
|
||||
if {[catch {set rlst $cmt_refn($sha1)}]} continue
|
||||
foreach refn $rlst {
|
||||
set inc($refn) 1
|
||||
}
|
||||
}
|
||||
close $fr_fd
|
||||
} else {
|
||||
foreach refn $all_refn {
|
||||
set inc($refn) 1
|
||||
}
|
||||
}
|
||||
|
||||
set spec_head [list]
|
||||
foreach name [load_all_heads] {
|
||||
lappend spec_head [list $name refs/heads/$name]
|
||||
set refn refs/heads/$name
|
||||
if {[info exists inc($refn)]} {
|
||||
lappend spec_head [list $name $refn]
|
||||
}
|
||||
}
|
||||
|
||||
set spec_trck [list]
|
||||
foreach spec [all_tracking_branches] {
|
||||
set name [lindex $spec 0]
|
||||
regsub ^refs/(heads|remotes)/ $name {} name
|
||||
lappend spec_trck [concat $name $spec]
|
||||
set refn [lindex $spec 0]
|
||||
if {[info exists inc($refn)]} {
|
||||
regsub ^refs/(heads|remotes)/ $refn {} name
|
||||
lappend spec_trck [concat $name $spec]
|
||||
}
|
||||
}
|
||||
|
||||
set spec_tag [list]
|
||||
foreach name [load_all_tags] {
|
||||
lappend spec_tag [list $name refs/tags/$name]
|
||||
set refn refs/tags/$name
|
||||
if {[info exists inc($refn)]} {
|
||||
lappend spec_tag [list $name $refn]
|
||||
}
|
||||
}
|
||||
|
||||
if {$is_detached} { set revtype HEAD
|
||||
@@ -364,4 +454,174 @@ method _sb_set {sb orient first last} {
|
||||
$sb set $first $last
|
||||
}
|
||||
|
||||
method _show_tooltip {pos} {
|
||||
if {$tooltip_wm ne {}} {
|
||||
_open_tooltip $this
|
||||
} elseif {$tooltip_timer eq {}} {
|
||||
set tooltip_timer [after 1000 [cb _open_tooltip]]
|
||||
}
|
||||
}
|
||||
|
||||
method _open_tooltip {} {
|
||||
global remote_url
|
||||
|
||||
set tooltip_timer {}
|
||||
set pos_x [winfo pointerx $w_list]
|
||||
set pos_y [winfo pointery $w_list]
|
||||
if {[winfo containing $pos_x $pos_y] ne $w_list} {
|
||||
_hide_tooltip $this
|
||||
return
|
||||
}
|
||||
|
||||
set pos @[join [list \
|
||||
[expr {$pos_x - [winfo rootx $w_list]}] \
|
||||
[expr {$pos_y - [winfo rooty $w_list]}]] ,]
|
||||
set lno [$w_list index $pos]
|
||||
if {$lno eq {}} {
|
||||
_hide_tooltip $this
|
||||
return
|
||||
}
|
||||
|
||||
set spec [lindex $cur_specs $lno]
|
||||
set refn [lindex $spec 1]
|
||||
if {$refn eq {}} {
|
||||
_hide_tooltip $this
|
||||
return
|
||||
}
|
||||
|
||||
if {$tooltip_wm eq {}} {
|
||||
set tooltip_wm [toplevel $w_list.tooltip -borderwidth 1]
|
||||
wm overrideredirect $tooltip_wm 1
|
||||
wm transient $tooltip_wm [winfo toplevel $w_list]
|
||||
set tooltip_t $tooltip_wm.label
|
||||
text $tooltip_t \
|
||||
-takefocus 0 \
|
||||
-highlightthickness 0 \
|
||||
-relief flat \
|
||||
-borderwidth 0 \
|
||||
-wrap none \
|
||||
-background lightyellow \
|
||||
-foreground black
|
||||
$tooltip_t tag conf section_header -font font_uibold
|
||||
bind $tooltip_wm <Escape> [cb _hide_tooltip]
|
||||
pack $tooltip_t
|
||||
} else {
|
||||
$tooltip_t conf -state normal
|
||||
$tooltip_t delete 0.0 end
|
||||
}
|
||||
|
||||
set data $tip_data($refn)
|
||||
if {[lindex $data 0 0] eq {tag}} {
|
||||
set tag [lindex $data 0]
|
||||
if {[lindex $data 1 0] eq {commit}} {
|
||||
set cmit [lindex $data 1]
|
||||
} else {
|
||||
set cmit {}
|
||||
}
|
||||
} elseif {[lindex $data 0 0] eq {commit}} {
|
||||
set tag {}
|
||||
set cmit [lindex $data 0]
|
||||
}
|
||||
|
||||
$tooltip_t insert end [lindex $spec 0]
|
||||
set last [_reflog_last $this [lindex $spec 1]]
|
||||
if {$last ne {}} {
|
||||
$tooltip_t insert end "\n"
|
||||
$tooltip_t insert end "updated"
|
||||
$tooltip_t insert end " $last"
|
||||
}
|
||||
$tooltip_t insert end "\n"
|
||||
|
||||
if {$tag ne {}} {
|
||||
$tooltip_t insert end "\n"
|
||||
$tooltip_t insert end "tag" section_header
|
||||
$tooltip_t insert end " [lindex $tag 1]\n"
|
||||
$tooltip_t insert end [lindex $tag 2]
|
||||
$tooltip_t insert end " ([lindex $tag 3])\n"
|
||||
$tooltip_t insert end [lindex $tag 4]
|
||||
$tooltip_t insert end "\n"
|
||||
}
|
||||
|
||||
if {$cmit ne {}} {
|
||||
$tooltip_t insert end "\n"
|
||||
$tooltip_t insert end "commit" section_header
|
||||
$tooltip_t insert end " [lindex $cmit 1]\n"
|
||||
$tooltip_t insert end [lindex $cmit 2]
|
||||
$tooltip_t insert end " ([lindex $cmit 3])\n"
|
||||
$tooltip_t insert end [lindex $cmit 4]
|
||||
}
|
||||
|
||||
if {[llength $spec] > 2} {
|
||||
$tooltip_t insert end "\n"
|
||||
$tooltip_t insert end "remote" section_header
|
||||
$tooltip_t insert end " [lindex $spec 2]\n"
|
||||
$tooltip_t insert end "url"
|
||||
$tooltip_t insert end " $remote_url([lindex $spec 2])\n"
|
||||
$tooltip_t insert end "branch"
|
||||
$tooltip_t insert end " [lindex $spec 3]"
|
||||
}
|
||||
|
||||
$tooltip_t conf -state disabled
|
||||
_position_tooltip $this
|
||||
}
|
||||
|
||||
method _reflog_last {name} {
|
||||
if {[info exists reflog_last($name)]} {
|
||||
return reflog_last($name)
|
||||
}
|
||||
|
||||
set last {}
|
||||
if {[catch {set last [file mtime [gitdir $name]]}]
|
||||
&& ![catch {set g [open [gitdir logs $name] r]}]} {
|
||||
fconfigure $g -translation binary
|
||||
while {[gets $g line] >= 0} {
|
||||
if {[regexp {> ([1-9][0-9]*) } $line line when]} {
|
||||
set last $when
|
||||
}
|
||||
}
|
||||
close $g
|
||||
}
|
||||
|
||||
if {$last ne {}} {
|
||||
set last [clock format $last -format {%a %b %e %H:%M:%S %Y}]
|
||||
}
|
||||
set reflog_last($name) $last
|
||||
return $last
|
||||
}
|
||||
|
||||
method _position_tooltip {} {
|
||||
set max_h [lindex [split [$tooltip_t index end] .] 0]
|
||||
set max_w 0
|
||||
for {set i 1} {$i <= $max_h} {incr i} {
|
||||
set c [lindex [split [$tooltip_t index "$i.0 lineend"] .] 1]
|
||||
if {$c > $max_w} {set max_w $c}
|
||||
}
|
||||
$tooltip_t conf -width $max_w -height $max_h
|
||||
|
||||
set req_w [winfo reqwidth $tooltip_t]
|
||||
set req_h [winfo reqheight $tooltip_t]
|
||||
set pos_x [expr {[winfo pointerx .] + 5}]
|
||||
set pos_y [expr {[winfo pointery .] + 10}]
|
||||
|
||||
set g "${req_w}x${req_h}"
|
||||
if {$pos_x >= 0} {append g +}
|
||||
append g $pos_x
|
||||
if {$pos_y >= 0} {append g +}
|
||||
append g $pos_y
|
||||
|
||||
wm geometry $tooltip_wm $g
|
||||
raise $tooltip_wm
|
||||
}
|
||||
|
||||
method _hide_tooltip {} {
|
||||
if {$tooltip_wm ne {}} {
|
||||
destroy $tooltip_wm
|
||||
set tooltip_wm {}
|
||||
}
|
||||
if {$tooltip_timer ne {}} {
|
||||
after cancel $tooltip_timer
|
||||
set tooltip_timer {}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,9 +37,14 @@ You are currently in the middle of a merge that has not been fully completed. Y
|
||||
set enc [string tolower [string range $line 9 end]]
|
||||
}
|
||||
}
|
||||
set msg [encoding convertfrom $enc [read $fd]]
|
||||
set msg [string trim $msg]
|
||||
set msg [read $fd]
|
||||
close $fd
|
||||
|
||||
set enc [tcl_encoding $enc]
|
||||
if {$enc ne {}} {
|
||||
set msg [encoding convertfrom $enc $msg]
|
||||
}
|
||||
set msg [string trim $msg]
|
||||
} err]} {
|
||||
error_popup "Error loading commit data for amend:\n\n$err"
|
||||
return
|
||||
@@ -148,7 +153,7 @@ The rescan will be automatically started now.
|
||||
U? {
|
||||
error_popup "Unmerged files cannot be committed.
|
||||
|
||||
File [short_path $path] has merge conflicts. You must resolve them and add the file before committing.
|
||||
File [short_path $path] has merge conflicts. You must resolve them and stage the file before committing.
|
||||
"
|
||||
unlock_index
|
||||
return
|
||||
@@ -164,7 +169,7 @@ File [short_path $path] cannot be committed by this program.
|
||||
if {!$files_ready && ![string match *merge $curType]} {
|
||||
info_popup {No changes to commit.
|
||||
|
||||
You must add at least 1 file before you can commit.
|
||||
You must stage at least 1 file before you can commit.
|
||||
}
|
||||
unlock_index
|
||||
return
|
||||
@@ -209,7 +214,7 @@ A good commit message has the following format:
|
||||
ui_status {Calling pre-commit hook...}
|
||||
set pch_error {}
|
||||
set fd_ph [open "| $pchook" r]
|
||||
fconfigure $fd_ph -blocking 0 -translation binary
|
||||
fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
|
||||
fileevent $fd_ph readable \
|
||||
[list commit_prehook_wait $fd_ph $curHEAD $msg]
|
||||
}
|
||||
@@ -287,11 +292,18 @@ A rescan will be automatically started now.
|
||||
#
|
||||
set msg_p [gitdir COMMIT_EDITMSG]
|
||||
set msg_wt [open $msg_p w]
|
||||
fconfigure $msg_wt -translation lf
|
||||
if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
|
||||
set enc utf-8
|
||||
}
|
||||
fconfigure $msg_wt -encoding binary -translation binary
|
||||
puts -nonewline $msg_wt [encoding convertto $enc $msg]
|
||||
set use_enc [tcl_encoding $enc]
|
||||
if {$use_enc ne {}} {
|
||||
fconfigure $msg_wt -encoding $use_enc
|
||||
} else {
|
||||
puts stderr "warning: Tcl does not support encoding '$enc'."
|
||||
fconfigure $msg_wt -encoding utf-8
|
||||
}
|
||||
puts -nonewline $msg_wt $msg
|
||||
close $msg_wt
|
||||
|
||||
# -- Create the commit.
|
||||
@@ -367,6 +379,10 @@ A rescan will be automatically started now.
|
||||
$ui_comm delete 0.0 end
|
||||
$ui_comm edit reset
|
||||
$ui_comm edit modified false
|
||||
if {$::GITGUI_BCK_exists} {
|
||||
catch {file delete [gitdir GITGUI_BCK]}
|
||||
set ::GITGUI_BCK_exists 0
|
||||
}
|
||||
|
||||
if {[is_enabled singlecommit]} do_quit
|
||||
|
||||
|
||||
@@ -87,3 +87,30 @@ proc do_fsck_objects {} {
|
||||
lappend cmd --strict
|
||||
console::exec $w $cmd
|
||||
}
|
||||
|
||||
proc hint_gc {} {
|
||||
set object_limit 8
|
||||
if {[is_Windows]} {
|
||||
set object_limit 1
|
||||
}
|
||||
|
||||
set objects_current [llength [glob \
|
||||
-directory [gitdir objects 42] \
|
||||
-nocomplain \
|
||||
-tails \
|
||||
-- \
|
||||
*]]
|
||||
|
||||
if {$objects_current >= $object_limit} {
|
||||
set objects_current [expr {$objects_current * 256}]
|
||||
set object_limit [expr {$object_limit * 256}]
|
||||
if {[ask_popup \
|
||||
"This repository currently has approximately $objects_current loose objects.
|
||||
|
||||
To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
|
||||
|
||||
Compress the database now?"] eq yes} {
|
||||
do_gc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ proc show_diff {path w {lno {}}} {
|
||||
set max_sz [expr {128 * 1024}]
|
||||
if {[catch {
|
||||
set fd [open $path r]
|
||||
fconfigure $fd -eofchar {}
|
||||
set content [read $fd $max_sz]
|
||||
close $fd
|
||||
set sz [file size $path]
|
||||
|
||||
276
git-gui/lib/encoding.tcl
Normal file
276
git-gui/lib/encoding.tcl
Normal file
@@ -0,0 +1,276 @@
|
||||
# git-gui encoding support
|
||||
# Copyright (C) 2005 Paul Mackerras <paulus@samba.org>
|
||||
# (Copied from gitk, commit fd8ccbec4f0161)
|
||||
|
||||
# This list of encoding names and aliases is distilled from
|
||||
# http://www.iana.org/assignments/character-sets.
|
||||
# Not all of them are supported by Tcl.
|
||||
set encoding_aliases {
|
||||
{ ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
|
||||
ISO646-US US-ASCII us IBM367 cp367 csASCII }
|
||||
{ ISO-10646-UTF-1 csISO10646UTF1 }
|
||||
{ ISO_646.basic:1983 ref csISO646basic1983 }
|
||||
{ INVARIANT csINVARIANT }
|
||||
{ ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
|
||||
{ BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
|
||||
{ NATS-SEFI iso-ir-8-1 csNATSSEFI }
|
||||
{ NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
|
||||
{ NATS-DANO iso-ir-9-1 csNATSDANO }
|
||||
{ NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
|
||||
{ SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
|
||||
{ SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
|
||||
{ KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
|
||||
{ ISO-2022-KR csISO2022KR }
|
||||
{ EUC-KR csEUCKR }
|
||||
{ ISO-2022-JP csISO2022JP }
|
||||
{ ISO-2022-JP-2 csISO2022JP2 }
|
||||
{ JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
|
||||
csISO13JISC6220jp }
|
||||
{ JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
|
||||
{ IT iso-ir-15 ISO646-IT csISO15Italian }
|
||||
{ PT iso-ir-16 ISO646-PT csISO16Portuguese }
|
||||
{ ES iso-ir-17 ISO646-ES csISO17Spanish }
|
||||
{ greek7-old iso-ir-18 csISO18Greek7Old }
|
||||
{ latin-greek iso-ir-19 csISO19LatinGreek }
|
||||
{ DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
|
||||
{ NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
|
||||
{ Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
|
||||
{ ISO_5427 iso-ir-37 csISO5427Cyrillic }
|
||||
{ JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
|
||||
{ BS_viewdata iso-ir-47 csISO47BSViewdata }
|
||||
{ INIS iso-ir-49 csISO49INIS }
|
||||
{ INIS-8 iso-ir-50 csISO50INIS8 }
|
||||
{ INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
|
||||
{ ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
|
||||
{ ISO_5428:1980 iso-ir-55 csISO5428Greek }
|
||||
{ GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
|
||||
{ GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
|
||||
{ NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
|
||||
csISO60Norwegian1 }
|
||||
{ NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
|
||||
{ NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
|
||||
{ videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
|
||||
{ PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
|
||||
{ ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
|
||||
{ MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
|
||||
{ JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
|
||||
{ greek7 iso-ir-88 csISO88Greek7 }
|
||||
{ ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
|
||||
{ iso-ir-90 csISO90 }
|
||||
{ JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
|
||||
{ JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
|
||||
csISO92JISC62991984b }
|
||||
{ JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
|
||||
{ JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
|
||||
{ JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
|
||||
csISO95JIS62291984handadd }
|
||||
{ JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
|
||||
{ ISO_2033-1983 iso-ir-98 e13b csISO2033 }
|
||||
{ ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
|
||||
{ ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
|
||||
CP819 csISOLatin1 }
|
||||
{ ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
|
||||
{ T.61-7bit iso-ir-102 csISO102T617bit }
|
||||
{ T.61-8bit T.61 iso-ir-103 csISO103T618bit }
|
||||
{ ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
|
||||
{ ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
|
||||
{ ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
|
||||
{ CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
|
||||
{ CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
|
||||
{ CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
|
||||
{ ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
|
||||
arabic csISOLatinArabic }
|
||||
{ ISO_8859-6-E csISO88596E ISO-8859-6-E }
|
||||
{ ISO_8859-6-I csISO88596I ISO-8859-6-I }
|
||||
{ ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
|
||||
greek greek8 csISOLatinGreek }
|
||||
{ T.101-G2 iso-ir-128 csISO128T101G2 }
|
||||
{ ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
|
||||
csISOLatinHebrew }
|
||||
{ ISO_8859-8-E csISO88598E ISO-8859-8-E }
|
||||
{ ISO_8859-8-I csISO88598I ISO-8859-8-I }
|
||||
{ CSN_369103 iso-ir-139 csISO139CSN369103 }
|
||||
{ JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
|
||||
{ ISO_6937-2-add iso-ir-142 csISOTextComm }
|
||||
{ IEC_P27-1 iso-ir-143 csISO143IECP271 }
|
||||
{ ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
|
||||
csISOLatinCyrillic }
|
||||
{ JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
|
||||
{ JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
|
||||
{ ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
|
||||
{ greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
|
||||
{ NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
|
||||
{ ISO_6937-2-25 iso-ir-152 csISO6937Add }
|
||||
{ GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
|
||||
{ ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
|
||||
{ ISO_10367-box iso-ir-155 csISO10367Box }
|
||||
{ ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
|
||||
{ latin-lap lap iso-ir-158 csISO158Lap }
|
||||
{ JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
|
||||
{ DS_2089 DS2089 ISO646-DK dk csISO646Danish }
|
||||
{ us-dk csUSDK }
|
||||
{ dk-us csDKUS }
|
||||
{ JIS_X0201 X0201 csHalfWidthKatakana }
|
||||
{ KSC5636 ISO646-KR csKSC5636 }
|
||||
{ ISO-10646-UCS-2 csUnicode }
|
||||
{ ISO-10646-UCS-4 csUCS4 }
|
||||
{ DEC-MCS dec csDECMCS }
|
||||
{ hp-roman8 roman8 r8 csHPRoman8 }
|
||||
{ macintosh mac csMacintosh }
|
||||
{ IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
|
||||
csIBM037 }
|
||||
{ IBM038 EBCDIC-INT cp038 csIBM038 }
|
||||
{ IBM273 CP273 csIBM273 }
|
||||
{ IBM274 EBCDIC-BE CP274 csIBM274 }
|
||||
{ IBM275 EBCDIC-BR cp275 csIBM275 }
|
||||
{ IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
|
||||
{ IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
|
||||
{ IBM280 CP280 ebcdic-cp-it csIBM280 }
|
||||
{ IBM281 EBCDIC-JP-E cp281 csIBM281 }
|
||||
{ IBM284 CP284 ebcdic-cp-es csIBM284 }
|
||||
{ IBM285 CP285 ebcdic-cp-gb csIBM285 }
|
||||
{ IBM290 cp290 EBCDIC-JP-kana csIBM290 }
|
||||
{ IBM297 cp297 ebcdic-cp-fr csIBM297 }
|
||||
{ IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
|
||||
{ IBM423 cp423 ebcdic-cp-gr csIBM423 }
|
||||
{ IBM424 cp424 ebcdic-cp-he csIBM424 }
|
||||
{ IBM437 cp437 437 csPC8CodePage437 }
|
||||
{ IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
|
||||
{ IBM775 cp775 csPC775Baltic }
|
||||
{ IBM850 cp850 850 csPC850Multilingual }
|
||||
{ IBM851 cp851 851 csIBM851 }
|
||||
{ IBM852 cp852 852 csPCp852 }
|
||||
{ IBM855 cp855 855 csIBM855 }
|
||||
{ IBM857 cp857 857 csIBM857 }
|
||||
{ IBM860 cp860 860 csIBM860 }
|
||||
{ IBM861 cp861 861 cp-is csIBM861 }
|
||||
{ IBM862 cp862 862 csPC862LatinHebrew }
|
||||
{ IBM863 cp863 863 csIBM863 }
|
||||
{ IBM864 cp864 csIBM864 }
|
||||
{ IBM865 cp865 865 csIBM865 }
|
||||
{ IBM866 cp866 866 csIBM866 }
|
||||
{ IBM868 CP868 cp-ar csIBM868 }
|
||||
{ IBM869 cp869 869 cp-gr csIBM869 }
|
||||
{ IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
|
||||
{ IBM871 CP871 ebcdic-cp-is csIBM871 }
|
||||
{ IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
|
||||
{ IBM891 cp891 csIBM891 }
|
||||
{ IBM903 cp903 csIBM903 }
|
||||
{ IBM904 cp904 904 csIBBM904 }
|
||||
{ IBM905 CP905 ebcdic-cp-tr csIBM905 }
|
||||
{ IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
|
||||
{ IBM1026 CP1026 csIBM1026 }
|
||||
{ EBCDIC-AT-DE csIBMEBCDICATDE }
|
||||
{ EBCDIC-AT-DE-A csEBCDICATDEA }
|
||||
{ EBCDIC-CA-FR csEBCDICCAFR }
|
||||
{ EBCDIC-DK-NO csEBCDICDKNO }
|
||||
{ EBCDIC-DK-NO-A csEBCDICDKNOA }
|
||||
{ EBCDIC-FI-SE csEBCDICFISE }
|
||||
{ EBCDIC-FI-SE-A csEBCDICFISEA }
|
||||
{ EBCDIC-FR csEBCDICFR }
|
||||
{ EBCDIC-IT csEBCDICIT }
|
||||
{ EBCDIC-PT csEBCDICPT }
|
||||
{ EBCDIC-ES csEBCDICES }
|
||||
{ EBCDIC-ES-A csEBCDICESA }
|
||||
{ EBCDIC-ES-S csEBCDICESS }
|
||||
{ EBCDIC-UK csEBCDICUK }
|
||||
{ EBCDIC-US csEBCDICUS }
|
||||
{ UNKNOWN-8BIT csUnknown8BiT }
|
||||
{ MNEMONIC csMnemonic }
|
||||
{ MNEM csMnem }
|
||||
{ VISCII csVISCII }
|
||||
{ VIQR csVIQR }
|
||||
{ KOI8-R csKOI8R }
|
||||
{ IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
|
||||
{ IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
|
||||
{ IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
|
||||
{ IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
|
||||
{ IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
|
||||
{ IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
|
||||
{ IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
|
||||
{ IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
|
||||
{ IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
|
||||
{ IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
|
||||
{ IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
|
||||
{ IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
|
||||
{ IBM1047 IBM-1047 }
|
||||
{ PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
|
||||
{ Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
|
||||
{ UNICODE-1-1 csUnicode11 }
|
||||
{ CESU-8 csCESU-8 }
|
||||
{ BOCU-1 csBOCU-1 }
|
||||
{ UNICODE-1-1-UTF-7 csUnicode11UTF7 }
|
||||
{ ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
|
||||
l8 }
|
||||
{ ISO-8859-15 ISO_8859-15 Latin-9 }
|
||||
{ ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
|
||||
{ GBK CP936 MS936 windows-936 }
|
||||
{ JIS_Encoding csJISEncoding }
|
||||
{ Shift_JIS MS_Kanji csShiftJIS }
|
||||
{ Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
|
||||
EUC-JP }
|
||||
{ Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
|
||||
{ ISO-10646-UCS-Basic csUnicodeASCII }
|
||||
{ ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
|
||||
{ ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
|
||||
{ ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
|
||||
{ ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
|
||||
{ ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
|
||||
{ ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
|
||||
{ ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
|
||||
{ ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
|
||||
{ ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
|
||||
{ ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
|
||||
{ Adobe-Standard-Encoding csAdobeStandardEncoding }
|
||||
{ Ventura-US csVenturaUS }
|
||||
{ Ventura-International csVenturaInternational }
|
||||
{ PC8-Danish-Norwegian csPC8DanishNorwegian }
|
||||
{ PC8-Turkish csPC8Turkish }
|
||||
{ IBM-Symbols csIBMSymbols }
|
||||
{ IBM-Thai csIBMThai }
|
||||
{ HP-Legal csHPLegal }
|
||||
{ HP-Pi-font csHPPiFont }
|
||||
{ HP-Math8 csHPMath8 }
|
||||
{ Adobe-Symbol-Encoding csHPPSMath }
|
||||
{ HP-DeskTop csHPDesktop }
|
||||
{ Ventura-Math csVenturaMath }
|
||||
{ Microsoft-Publishing csMicrosoftPublishing }
|
||||
{ Windows-31J csWindows31J }
|
||||
{ GB2312 csGB2312 }
|
||||
{ Big5 csBig5 }
|
||||
}
|
||||
|
||||
proc tcl_encoding {enc} {
|
||||
global encoding_aliases
|
||||
set names [encoding names]
|
||||
set lcnames [string tolower $names]
|
||||
set enc [string tolower $enc]
|
||||
set i [lsearch -exact $lcnames $enc]
|
||||
if {$i < 0} {
|
||||
# look for "isonnn" instead of "iso-nnn" or "iso_nnn"
|
||||
if {[regsub {^iso[-_]} $enc iso encx]} {
|
||||
set i [lsearch -exact $lcnames $encx]
|
||||
}
|
||||
}
|
||||
if {$i < 0} {
|
||||
foreach l $encoding_aliases {
|
||||
set ll [string tolower $l]
|
||||
if {[lsearch -exact $ll $enc] < 0} continue
|
||||
# look through the aliases for one that tcl knows about
|
||||
foreach e $ll {
|
||||
set i [lsearch -exact $lcnames $e]
|
||||
if {$i < 0} {
|
||||
if {[regsub {^iso[-_]} $e iso ex]} {
|
||||
set i [lsearch -exact $lcnames $ex]
|
||||
}
|
||||
}
|
||||
if {$i >= 0} break
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if {$i >= 0} {
|
||||
return [lindex $names $i]
|
||||
}
|
||||
return {}
|
||||
}
|
||||
@@ -51,12 +51,15 @@ proc ask_popup {msg} {
|
||||
if {[reponame] ne {}} {
|
||||
append title " ([reponame])"
|
||||
}
|
||||
return [tk_messageBox \
|
||||
-parent . \
|
||||
set cmd [list tk_messageBox \
|
||||
-icon question \
|
||||
-type yesno \
|
||||
-title $title \
|
||||
-message $msg]
|
||||
if {[winfo ismapped .]} {
|
||||
lappend cmd -parent .
|
||||
}
|
||||
eval $cmd
|
||||
}
|
||||
|
||||
proc hook_failed_popup {hook msg} {
|
||||
|
||||
@@ -360,7 +360,7 @@ proc revert_helper {txt paths} {
|
||||
"[appname] ([reponame])" \
|
||||
"Revert changes in $s?
|
||||
|
||||
Any unadded changes will be permanently lost by the revert." \
|
||||
Any unstaged changes will be permanently lost by the revert." \
|
||||
question \
|
||||
1 \
|
||||
{Do Nothing} \
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# git-gui branch merge support
|
||||
# Copyright (C) 2006, 2007 Shawn Pearce
|
||||
|
||||
namespace eval merge {
|
||||
class merge {
|
||||
|
||||
proc _can_merge {} {
|
||||
field w ; # top level window
|
||||
field w_rev ; # mega-widget to pick the revision to merge
|
||||
|
||||
method _can_merge {} {
|
||||
global HEAD commit_type file_states
|
||||
|
||||
if {[string match amend* $commit_type]} {
|
||||
@@ -42,7 +45,7 @@ The rescan will be automatically started now.
|
||||
|
||||
File [short_path $path] has merge conflicts.
|
||||
|
||||
You must resolve them, add the file, and commit to complete the current merge. Only then can you begin another merge.
|
||||
You must resolve them, stage the file, and commit to complete the current merge. Only then can you begin another merge.
|
||||
"
|
||||
unlock_index
|
||||
return 0
|
||||
@@ -63,147 +66,93 @@ You should complete the current commit before starting a merge. Doing so will h
|
||||
return 1
|
||||
}
|
||||
|
||||
proc _refs {w list} {
|
||||
set r {}
|
||||
foreach i [$w.source.l curselection] {
|
||||
lappend r [lindex [lindex $list $i] 0]
|
||||
method _rev {} {
|
||||
if {[catch {$w_rev commit_or_die}]} {
|
||||
return {}
|
||||
}
|
||||
return $r
|
||||
return [$w_rev get]
|
||||
}
|
||||
|
||||
proc _visualize {w list} {
|
||||
set revs [_refs $w $list]
|
||||
if {$revs eq {}} return
|
||||
lappend revs --not HEAD
|
||||
do_gitk $revs
|
||||
method _visualize {} {
|
||||
set rev [_rev $this]
|
||||
if {$rev ne {}} {
|
||||
do_gitk [list $rev --not HEAD]
|
||||
}
|
||||
}
|
||||
|
||||
proc _start {w list} {
|
||||
global HEAD current_branch
|
||||
method _start {} {
|
||||
global HEAD current_branch remote_url
|
||||
|
||||
set cmd [list git merge]
|
||||
set names [_refs $w $list]
|
||||
set revcnt [llength $names]
|
||||
append cmd { } $names
|
||||
|
||||
if {$revcnt == 0} {
|
||||
set name [_rev $this]
|
||||
if {$name eq {}} {
|
||||
return
|
||||
} elseif {$revcnt == 1} {
|
||||
set unit branch
|
||||
} elseif {$revcnt <= 15} {
|
||||
set unit branches
|
||||
}
|
||||
|
||||
if {[tk_dialog \
|
||||
$w.confirm_octopus \
|
||||
[wm title $w] \
|
||||
"Use octopus merge strategy?
|
||||
set spec [$w_rev get_tracking_branch]
|
||||
set cmit [$w_rev get_commit]
|
||||
|
||||
You are merging $revcnt branches at once. This requires using the octopus merge driver, which may not succeed if there are file-level conflicts.
|
||||
" \
|
||||
question \
|
||||
0 \
|
||||
{Cancel} \
|
||||
{Use octopus} \
|
||||
] != 1} return
|
||||
set fh [open [gitdir FETCH_HEAD] w]
|
||||
fconfigure $fh -translation lf
|
||||
if {$spec eq {}} {
|
||||
set remote .
|
||||
set branch $name
|
||||
set stitle $branch
|
||||
} else {
|
||||
tk_messageBox \
|
||||
-icon error \
|
||||
-type ok \
|
||||
-title [wm title $w] \
|
||||
-parent $w \
|
||||
-message "Too many branches selected.
|
||||
|
||||
You have requested to merge $revcnt branches in an octopus merge. This exceeds Git's internal limit of 15 branches per merge.
|
||||
|
||||
Please select fewer branches. To merge more than 15 branches, merge the branches in batches.
|
||||
"
|
||||
return
|
||||
set remote $remote_url([lindex $spec 1])
|
||||
if {[regexp {^[^:@]*@[^:]*:/} $remote]} {
|
||||
regsub {^[^:@]*@} $remote {} remote
|
||||
}
|
||||
set branch [lindex $spec 2]
|
||||
set stitle "$branch of $remote"
|
||||
}
|
||||
regsub ^refs/heads/ $branch {} branch
|
||||
puts $fh "$cmit\t\tbranch '$branch' of $remote"
|
||||
close $fh
|
||||
|
||||
set msg "Merging $current_branch, [join $names {, }]"
|
||||
set cmd [list git]
|
||||
lappend cmd merge
|
||||
lappend cmd --strategy=recursive
|
||||
lappend cmd [git fmt-merge-msg <[gitdir FETCH_HEAD]]
|
||||
lappend cmd HEAD
|
||||
lappend cmd $cmit
|
||||
|
||||
set msg "Merging $current_branch and $stitle"
|
||||
ui_status "$msg..."
|
||||
set cons [console::new "Merge" $msg]
|
||||
console::exec $cons $cmd \
|
||||
[namespace code [list _finish $revcnt $cons]]
|
||||
set cons [console::new "Merge" "merge $stitle"]
|
||||
console::exec $cons $cmd [cb _finish $cons]
|
||||
|
||||
wm protocol $w WM_DELETE_WINDOW {}
|
||||
destroy $w
|
||||
}
|
||||
|
||||
proc _finish {revcnt w ok} {
|
||||
console::done $w $ok
|
||||
method _finish {cons ok} {
|
||||
console::done $cons $ok
|
||||
if {$ok} {
|
||||
set msg {Merge completed successfully.}
|
||||
} else {
|
||||
if {$revcnt != 1} {
|
||||
info_popup "Octopus merge failed.
|
||||
|
||||
Your merge of $revcnt branches has failed.
|
||||
|
||||
There are file-level conflicts between the branches which must be resolved manually.
|
||||
|
||||
The working directory will now be reset.
|
||||
|
||||
You can attempt this merge again by merging only one branch at a time." $w
|
||||
|
||||
set fd [git_read read-tree --reset -u HEAD]
|
||||
fconfigure $fd -blocking 0 -translation binary
|
||||
fileevent $fd readable \
|
||||
[namespace code [list _reset_wait $fd]]
|
||||
ui_status {Aborting... please wait...}
|
||||
return
|
||||
}
|
||||
|
||||
set msg {Merge failed. Conflict resolution is required.}
|
||||
}
|
||||
unlock_index
|
||||
rescan [list ui_status $msg]
|
||||
delete_this
|
||||
}
|
||||
|
||||
proc dialog {} {
|
||||
constructor dialog {} {
|
||||
global current_branch
|
||||
global M1B
|
||||
|
||||
if {![_can_merge]} return
|
||||
|
||||
set fmt {list %(objectname) %(*objectname) %(refname) %(subject)}
|
||||
set fr_fd [git_read for-each-ref \
|
||||
--tcl \
|
||||
--format=$fmt \
|
||||
refs/heads \
|
||||
refs/remotes \
|
||||
refs/tags \
|
||||
]
|
||||
fconfigure $fr_fd -translation binary
|
||||
while {[gets $fr_fd line] > 0} {
|
||||
set line [eval $line]
|
||||
set ref [lindex $line 2]
|
||||
regsub ^refs/(heads|remotes|tags)/ $ref {} ref
|
||||
set subj($ref) [lindex $line 3]
|
||||
lappend sha1([lindex $line 0]) $ref
|
||||
if {[lindex $line 1] ne {}} {
|
||||
lappend sha1([lindex $line 1]) $ref
|
||||
}
|
||||
if {![_can_merge $this]} {
|
||||
delete_this
|
||||
return
|
||||
}
|
||||
close $fr_fd
|
||||
|
||||
set to_show {}
|
||||
set fr_fd [git_read rev-list --all --not HEAD]
|
||||
while {[gets $fr_fd line] > 0} {
|
||||
if {[catch {set ref $sha1($line)}]} continue
|
||||
foreach n $ref {
|
||||
lappend to_show [list $n $line]
|
||||
}
|
||||
make_toplevel top w
|
||||
wm title $top "[appname] ([reponame]): Merge"
|
||||
if {$top ne {.}} {
|
||||
wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
|
||||
}
|
||||
close $fr_fd
|
||||
set to_show [lsort -unique $to_show]
|
||||
|
||||
set w .merge_setup
|
||||
toplevel $w
|
||||
wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
|
||||
|
||||
set _visualize [namespace code [list _visualize $w $to_show]]
|
||||
set _start [namespace code [list _start $w $to_show]]
|
||||
set _start [cb _start]
|
||||
|
||||
label $w.header \
|
||||
-text "Merge Into $current_branch" \
|
||||
@@ -211,55 +160,51 @@ proc dialog {} {
|
||||
pack $w.header -side top -fill x
|
||||
|
||||
frame $w.buttons
|
||||
button $w.buttons.visualize -text Visualize -command $_visualize
|
||||
button $w.buttons.visualize \
|
||||
-text Visualize \
|
||||
-command [cb _visualize]
|
||||
pack $w.buttons.visualize -side left
|
||||
button $w.buttons.create -text Merge -command $_start
|
||||
pack $w.buttons.create -side right
|
||||
button $w.buttons.merge \
|
||||
-text Merge \
|
||||
-command $_start
|
||||
pack $w.buttons.merge -side right
|
||||
button $w.buttons.cancel \
|
||||
-text {Cancel} \
|
||||
-command "unlock_index;destroy $w"
|
||||
-command [cb _cancel]
|
||||
pack $w.buttons.cancel -side right -padx 5
|
||||
pack $w.buttons -side bottom -fill x -pady 10 -padx 10
|
||||
|
||||
labelframe $w.source -text {Source Branches}
|
||||
listbox $w.source.l \
|
||||
-height 10 \
|
||||
-width 70 \
|
||||
-font font_diff \
|
||||
-selectmode extended \
|
||||
-yscrollcommand [list $w.source.sby set]
|
||||
scrollbar $w.source.sby -command [list $w.source.l yview]
|
||||
pack $w.source.sby -side right -fill y
|
||||
pack $w.source.l -side left -fill both -expand 1
|
||||
pack $w.source -fill both -expand 1 -pady 5 -padx 5
|
||||
|
||||
foreach ref $to_show {
|
||||
set n [lindex $ref 0]
|
||||
if {[string length $n] > 20} {
|
||||
set n "[string range $n 0 16]..."
|
||||
}
|
||||
$w.source.l insert end [format {%s %-20s %s} \
|
||||
[string range [lindex $ref 1] 0 5] \
|
||||
$n \
|
||||
$subj([lindex $ref 0])]
|
||||
}
|
||||
|
||||
bind $w.source.l <Key-K> [list event generate %W <Shift-Key-Up>]
|
||||
bind $w.source.l <Key-J> [list event generate %W <Shift-Key-Down>]
|
||||
bind $w.source.l <Key-k> [list event generate %W <Key-Up>]
|
||||
bind $w.source.l <Key-j> [list event generate %W <Key-Down>]
|
||||
bind $w.source.l <Key-h> [list event generate %W <Key-Left>]
|
||||
bind $w.source.l <Key-l> [list event generate %W <Key-Right>]
|
||||
bind $w.source.l <Key-v> $_visualize
|
||||
set w_rev [::choose_rev::new_unmerged $w.rev {Revision To Merge}]
|
||||
pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
|
||||
|
||||
bind $w <$M1B-Key-Return> $_start
|
||||
bind $w <Visibility> "grab $w; focus $w.source.l"
|
||||
bind $w <Key-Escape> "unlock_index;destroy $w"
|
||||
wm protocol $w WM_DELETE_WINDOW "unlock_index;destroy $w"
|
||||
wm title $w "[appname] ([reponame]): Merge"
|
||||
bind $w <Key-Return> $_start
|
||||
bind $w <Key-Escape> [cb _cancel]
|
||||
wm protocol $w WM_DELETE_WINDOW [cb _cancel]
|
||||
|
||||
bind $w.buttons.merge <Visibility> [cb _visible]
|
||||
tkwait window $w
|
||||
}
|
||||
|
||||
method _visible {} {
|
||||
grab $w
|
||||
if {[is_config_true gui.matchtrackingbranch]} {
|
||||
$w_rev pick_tracking_branch
|
||||
}
|
||||
$w_rev focus_filter
|
||||
}
|
||||
|
||||
method _cancel {} {
|
||||
wm protocol $w WM_DELETE_WINDOW {}
|
||||
unlock_index
|
||||
destroy $w
|
||||
delete_this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace eval merge {
|
||||
|
||||
proc reset_hard {} {
|
||||
global HEAD commit_type file_states
|
||||
|
||||
@@ -274,20 +219,24 @@ You must finish amending this commit.
|
||||
if {![lock_index abort]} return
|
||||
|
||||
if {[string match *merge* $commit_type]} {
|
||||
set op merge
|
||||
set op_question "Abort merge?
|
||||
|
||||
Aborting the current merge will cause *ALL* uncommitted changes to be lost.
|
||||
|
||||
Continue with aborting the current merge?"
|
||||
} else {
|
||||
set op commit
|
||||
set op_question "Reset changes?
|
||||
|
||||
Resetting the changes will cause *ALL* uncommitted changes to be lost.
|
||||
|
||||
Continue with resetting the current changes?"
|
||||
}
|
||||
|
||||
if {[ask_popup "Abort $op?
|
||||
|
||||
Aborting the current $op will cause *ALL* uncommitted changes to be lost.
|
||||
|
||||
Continue with aborting the current $op?"] eq {yes}} {
|
||||
set fd [git_read read-tree --reset -u HEAD]
|
||||
if {[ask_popup $op_question] eq {yes}} {
|
||||
set fd [git_read --stderr read-tree --reset -u -v HEAD]
|
||||
fconfigure $fd -blocking 0 -translation binary
|
||||
fileevent $fd readable [namespace code [list _reset_wait $fd]]
|
||||
ui_status {Aborting... please wait...}
|
||||
$::main_status start {Aborting} {files reset}
|
||||
} else {
|
||||
unlock_index
|
||||
}
|
||||
@@ -296,9 +245,12 @@ Continue with aborting the current $op?"] eq {yes}} {
|
||||
proc _reset_wait {fd} {
|
||||
global ui_comm
|
||||
|
||||
read $fd
|
||||
$::main_status update_meter [read $fd]
|
||||
|
||||
fconfigure $fd -blocking 1
|
||||
if {[eof $fd]} {
|
||||
close $fd
|
||||
set fail [catch {close $fd} err]
|
||||
$::main_status stop
|
||||
unlock_index
|
||||
|
||||
$ui_comm delete 0.0 end
|
||||
@@ -310,7 +262,12 @@ proc _reset_wait {fd} {
|
||||
catch {file delete [gitdir MERGE_MSG]}
|
||||
catch {file delete [gitdir GITGUI_MSG]}
|
||||
|
||||
if {$fail} {
|
||||
warn_popup "Abort failed.\n\n$err"
|
||||
}
|
||||
rescan {ui_status {Abort completed. Ready.}}
|
||||
} else {
|
||||
fconfigure $fd -blocking 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ proc all_tracking_branches {} {
|
||||
proc load_all_remotes {} {
|
||||
global repo_config
|
||||
global all_remotes tracking_branches some_heads_tracking
|
||||
global remote_url
|
||||
|
||||
set some_heads_tracking 0
|
||||
set all_remotes [list]
|
||||
@@ -76,6 +77,10 @@ proc load_all_remotes {} {
|
||||
catch {
|
||||
set fd [open [file join $rm_dir $name] r]
|
||||
while {[gets $fd line] >= 0} {
|
||||
if {[regexp {^URL:[ ]*(.+)$} $line line url]} {
|
||||
set remote_url($name) $url
|
||||
continue
|
||||
}
|
||||
if {![regexp {^Pull:[ ]*([^:]+):(.+)$} \
|
||||
$line line src dst]} continue
|
||||
if {[string index $src 0] eq {+}} {
|
||||
@@ -100,6 +105,7 @@ proc load_all_remotes {} {
|
||||
foreach line [array names repo_config remote.*.url] {
|
||||
if {![regexp ^remote\.(.*)\.url\$ $line line name]} continue
|
||||
lappend all_remotes $name
|
||||
set remote_url($name) $repo_config(remote.$name.url)
|
||||
|
||||
if {[catch {set fl $repo_config(remote.$name.fetch)}]} {
|
||||
set fl {}
|
||||
|
||||
@@ -17,8 +17,10 @@ USAGE='(--continue | --abort | --skip | [--preserve-merges] [--verbose]
|
||||
require_work_tree
|
||||
|
||||
DOTEST="$GIT_DIR/.dotest-merge"
|
||||
TODO="$DOTEST"/todo
|
||||
TODO="$DOTEST"/git-rebase-todo
|
||||
DONE="$DOTEST"/done
|
||||
MSG="$DOTEST"/message
|
||||
SQUASH_MSG="$DOTEST"/message-squash
|
||||
REWRITTEN="$DOTEST"/rewritten
|
||||
PRESERVE_MERGES=
|
||||
STRATEGY=
|
||||
@@ -31,6 +33,20 @@ warn () {
|
||||
echo "$*" >&2
|
||||
}
|
||||
|
||||
output () {
|
||||
case "$VERBOSE" in
|
||||
'')
|
||||
"$@" > "$DOTEST"/output 2>&1
|
||||
status=$?
|
||||
test $status != 0 &&
|
||||
cat "$DOTEST"/output
|
||||
return $status
|
||||
;;
|
||||
*)
|
||||
"$@"
|
||||
esac
|
||||
}
|
||||
|
||||
require_clean_work_tree () {
|
||||
# test if working tree is dirty
|
||||
git rev-parse --verify HEAD > /dev/null &&
|
||||
@@ -54,6 +70,10 @@ mark_action_done () {
|
||||
sed -e 1q < "$TODO" >> "$DONE"
|
||||
sed -e 1d < "$TODO" >> "$TODO".new
|
||||
mv -f "$TODO".new "$TODO"
|
||||
count=$(($(wc -l < "$DONE")))
|
||||
total=$(($count+$(wc -l < "$TODO")))
|
||||
printf "Rebasing (%d/%d)\r" $count $total
|
||||
test -z "$VERBOSE" || echo
|
||||
}
|
||||
|
||||
make_patch () {
|
||||
@@ -77,18 +97,18 @@ die_abort () {
|
||||
|
||||
pick_one () {
|
||||
case "$1" in -n) sha1=$2 ;; *) sha1=$1 ;; esac
|
||||
git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
|
||||
output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
|
||||
test -d "$REWRITTEN" &&
|
||||
pick_one_preserving_merges "$@" && return
|
||||
parent_sha1=$(git rev-parse --verify $sha1^ 2>/dev/null)
|
||||
current_sha1=$(git rev-parse --verify HEAD)
|
||||
if [ $current_sha1 = $parent_sha1 ]; then
|
||||
git reset --hard $sha1
|
||||
test "a$1" = a-n && git reset --soft $current_sha1
|
||||
if test $current_sha1 = $parent_sha1; then
|
||||
output git reset --hard $sha1
|
||||
test "a$1" = a-n && output git reset --soft $current_sha1
|
||||
sha1=$(git rev-parse --short $sha1)
|
||||
warn Fast forward to $sha1
|
||||
output warn Fast forward to $sha1
|
||||
else
|
||||
git cherry-pick $STRATEGY "$@"
|
||||
output git cherry-pick $STRATEGY "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -96,7 +116,7 @@ pick_one_preserving_merges () {
|
||||
case "$1" in -n) sha1=$2 ;; *) sha1=$1 ;; esac
|
||||
sha1=$(git rev-parse $sha1)
|
||||
|
||||
if [ -f "$DOTEST"/current-commit ]
|
||||
if test -f "$DOTEST"/current-commit
|
||||
then
|
||||
current_commit=$(cat "$DOTEST"/current-commit) &&
|
||||
git rev-parse HEAD > "$REWRITTEN"/$current_commit &&
|
||||
@@ -110,7 +130,7 @@ pick_one_preserving_merges () {
|
||||
new_parents=
|
||||
for p in $(git rev-list --parents -1 $sha1 | cut -d\ -f2-)
|
||||
do
|
||||
if [ -f "$REWRITTEN"/$p ]
|
||||
if test -f "$REWRITTEN"/$p
|
||||
then
|
||||
preserve=f
|
||||
new_p=$(cat "$REWRITTEN"/$p)
|
||||
@@ -125,7 +145,7 @@ pick_one_preserving_merges () {
|
||||
done
|
||||
case $fast_forward in
|
||||
t)
|
||||
echo "Fast forward to $sha1"
|
||||
output warn "Fast forward to $sha1"
|
||||
test $preserve=f && echo $sha1 > "$REWRITTEN"/$sha1
|
||||
;;
|
||||
f)
|
||||
@@ -133,7 +153,7 @@ pick_one_preserving_merges () {
|
||||
|
||||
first_parent=$(expr "$new_parents" : " \([^ ]*\)")
|
||||
# detach HEAD to current parent
|
||||
git checkout $first_parent 2> /dev/null ||
|
||||
output git checkout $first_parent 2> /dev/null ||
|
||||
die "Cannot move HEAD to $first_parent"
|
||||
|
||||
echo $sha1 > "$DOTEST"/current-commit
|
||||
@@ -145,19 +165,51 @@ pick_one_preserving_merges () {
|
||||
msg="$(git cat-file commit $sha1 | \
|
||||
sed -e '1,/^$/d' -e "s/[\"\\]/\\\\&/g")"
|
||||
# NEEDSWORK: give rerere a chance
|
||||
if ! git merge $STRATEGY -m "$msg" $new_parents
|
||||
if ! output git merge $STRATEGY -m "$msg" $new_parents
|
||||
then
|
||||
echo "$msg" > "$GIT_DIR"/MERGE_MSG
|
||||
die Error redoing merge $sha1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
git cherry-pick $STRATEGY "$@" ||
|
||||
output git cherry-pick $STRATEGY "$@" ||
|
||||
die_with_patch $sha1 "Could not pick $sha1"
|
||||
esac
|
||||
esac
|
||||
}
|
||||
|
||||
nth_string () {
|
||||
case "$1" in
|
||||
*1[0-9]|*[04-9]) echo "$1"th;;
|
||||
*1) echo "$1"st;;
|
||||
*2) echo "$1"nd;;
|
||||
*3) echo "$1"rd;;
|
||||
esac
|
||||
}
|
||||
|
||||
make_squash_message () {
|
||||
if test -f "$SQUASH_MSG"; then
|
||||
COUNT=$(($(sed -n "s/^# This is [^0-9]*\([0-9]\+\).*/\1/p" \
|
||||
< "$SQUASH_MSG" | tail -n 1)+1))
|
||||
echo "# This is a combination of $COUNT commits."
|
||||
sed -n "2,\$p" < "$SQUASH_MSG"
|
||||
else
|
||||
COUNT=2
|
||||
echo "# This is a combination of two commits."
|
||||
echo "# The first commit's message is:"
|
||||
echo
|
||||
git cat-file commit HEAD | sed -e '1,/^$/d'
|
||||
echo
|
||||
fi
|
||||
echo "# This is the $(nth_string $COUNT) commit message:"
|
||||
echo
|
||||
git cat-file commit $1 | sed -e '1,/^$/d'
|
||||
}
|
||||
|
||||
peek_next_command () {
|
||||
sed -n "1s/ .*$//p" < "$TODO"
|
||||
}
|
||||
|
||||
do_next () {
|
||||
test -f "$DOTEST"/message && rm "$DOTEST"/message
|
||||
test -f "$DOTEST"/author-script && rm "$DOTEST"/author-script
|
||||
@@ -194,18 +246,22 @@ do_next () {
|
||||
die "Cannot 'squash' without a previous commit"
|
||||
|
||||
mark_action_done
|
||||
MSG="$DOTEST"/message
|
||||
echo "# This is a combination of two commits." > "$MSG"
|
||||
echo "# The first commit's message is:" >> "$MSG"
|
||||
echo >> "$MSG"
|
||||
git cat-file commit HEAD | sed -e '1,/^$/d' >> "$MSG"
|
||||
echo >> "$MSG"
|
||||
make_squash_message $sha1 > "$MSG"
|
||||
case "$(peek_next_command)" in
|
||||
squash)
|
||||
EDIT_COMMIT=
|
||||
USE_OUTPUT=output
|
||||
cp "$MSG" "$SQUASH_MSG"
|
||||
;;
|
||||
*)
|
||||
EDIT_COMMIT=-e
|
||||
USE_OUTPUT=
|
||||
test -f "$SQUASH_MSG" && rm "$SQUASH_MSG"
|
||||
esac
|
||||
|
||||
failed=f
|
||||
output git reset --soft HEAD^
|
||||
pick_one -n $sha1 || failed=t
|
||||
echo "# And this is the 2nd commit message:" >> "$MSG"
|
||||
echo >> "$MSG"
|
||||
git cat-file commit $sha1 | sed -e '1,/^$/d' >> "$MSG"
|
||||
git reset --soft HEAD^
|
||||
author_script=$(get_author_ident_from_commit $sha1)
|
||||
echo "$author_script" > "$DOTEST"/author-script
|
||||
case $failed in
|
||||
@@ -213,7 +269,7 @@ do_next () {
|
||||
# This is like --amend, but with a different message
|
||||
eval "$author_script"
|
||||
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
|
||||
git commit -F "$MSG" -e
|
||||
$USE_OUTPUT git commit -F "$MSG" $EDIT_COMMIT
|
||||
;;
|
||||
t)
|
||||
cp "$MSG" "$GIT_DIR"/MERGE_MSG
|
||||
@@ -232,7 +288,7 @@ do_next () {
|
||||
HEADNAME=$(cat "$DOTEST"/head-name) &&
|
||||
OLDHEAD=$(cat "$DOTEST"/head) &&
|
||||
SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) &&
|
||||
if [ -d "$REWRITTEN" ]
|
||||
if test -d "$REWRITTEN"
|
||||
then
|
||||
test -f "$DOTEST"/current-commit &&
|
||||
current_commit=$(cat "$DOTEST"/current-commit) &&
|
||||
@@ -288,7 +344,7 @@ do
|
||||
HEADNAME=$(cat "$DOTEST"/head-name)
|
||||
HEAD=$(cat "$DOTEST"/head)
|
||||
git symbolic-ref HEAD $HEADNAME &&
|
||||
git reset --hard $HEAD &&
|
||||
output git reset --hard $HEAD &&
|
||||
rm -rf "$DOTEST"
|
||||
exit
|
||||
;;
|
||||
@@ -297,7 +353,7 @@ do
|
||||
|
||||
test -d "$DOTEST" || die "No interactive rebase running"
|
||||
|
||||
git reset --hard && do_rest
|
||||
output git reset --hard && do_rest
|
||||
;;
|
||||
-s|--strategy)
|
||||
shift
|
||||
@@ -349,11 +405,11 @@ do
|
||||
|
||||
require_clean_work_tree
|
||||
|
||||
if [ ! -z "$2"]
|
||||
if test ! -z "$2"
|
||||
then
|
||||
git show-ref --verify --quiet "refs/heads/$2" ||
|
||||
output git show-ref --verify --quiet "refs/heads/$2" ||
|
||||
die "Invalid branchname: $2"
|
||||
git checkout "$2" ||
|
||||
output git checkout "$2" ||
|
||||
die "Could not checkout $2"
|
||||
fi
|
||||
|
||||
@@ -372,7 +428,7 @@ do
|
||||
echo $ONTO > "$DOTEST"/onto
|
||||
test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy
|
||||
test t = "$VERBOSE" && : > "$DOTEST"/verbose
|
||||
if [ t = "$PRESERVE_MERGES" ]
|
||||
if test t = "$PRESERVE_MERGES"
|
||||
then
|
||||
# $REWRITTEN contains files for each commit that is
|
||||
# reachable by at least one merge base of $HEAD and
|
||||
@@ -414,13 +470,13 @@ EOF
|
||||
die_abort "Nothing to do"
|
||||
|
||||
cp "$TODO" "$TODO".backup
|
||||
${VISUAL:-${EDITOR:-vi}} "$TODO" ||
|
||||
git_editor "$TODO" ||
|
||||
die "Could not execute editor"
|
||||
|
||||
test -z "$(grep -ve '^$' -e '^#' < $TODO)" &&
|
||||
die_abort "Nothing to do"
|
||||
|
||||
git checkout $ONTO && do_rest
|
||||
output git checkout $ONTO && do_rest
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
@@ -49,8 +49,8 @@ Options:
|
||||
--bcc Specify a list of email addresses that should be Bcc:
|
||||
on all the emails.
|
||||
|
||||
--compose Use \$EDITOR to edit an introductory message for the
|
||||
patch series.
|
||||
--compose Use \$GIT_EDITOR, core.editor, \$EDITOR, or \$VISUAL to edit
|
||||
an introductory message for the patch series.
|
||||
|
||||
--subject Specify the initial "Subject:" line.
|
||||
Only necessary if --compose is also set. If --compose
|
||||
@@ -237,7 +237,7 @@ my %parse_alias = (
|
||||
$aliases{$1} = [ split(/\s+/, $2) ];
|
||||
}}},
|
||||
pine => sub { my $fh = shift; while (<$fh>) {
|
||||
if (/^(\S+)\s+(.*)$/) {
|
||||
if (/^(\S+)\t.*\t(.*)$/) {
|
||||
$aliases{$1} = [ split(/\s*,\s*/, $2) ];
|
||||
}}},
|
||||
gnus => sub { my $fh = shift; while (<$fh>) {
|
||||
@@ -341,8 +341,7 @@ GIT: for the patch you are writing.
|
||||
EOT
|
||||
close(C);
|
||||
|
||||
my $editor = $ENV{EDITOR};
|
||||
$editor = 'vi' unless defined $editor;
|
||||
my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
|
||||
system($editor, $compose_filename);
|
||||
|
||||
open(C2,">",$compose_filename . ".final")
|
||||
|
||||
@@ -28,6 +28,21 @@ set_reflog_action() {
|
||||
fi
|
||||
}
|
||||
|
||||
git_editor() {
|
||||
GIT_EDITOR=${GIT_EDITOR:-$(git config core.editor || echo ${VISUAL:-${EDITOR}})}
|
||||
case "$GIT_EDITOR,$TERM" in
|
||||
,dumb)
|
||||
echo >&2 "No editor specified in GIT_EDITOR, core.editor, VISUAL,"
|
||||
echo >&2 "or EDITOR. Tried to fall back to vi but terminal is dumb."
|
||||
echo >&2 "Please set one of these variables to an appropriate"
|
||||
echo >&2 "editor or run $0 with options that will not cause an"
|
||||
echo >&2 "editor to be invoked (e.g., -m or -F for git-commit)."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
"${GIT_EDITOR:-vi}" "$1"
|
||||
}
|
||||
|
||||
is_bare_repository () {
|
||||
git rev-parse --is-bare-repository
|
||||
}
|
||||
|
||||
47
git-stash.sh
47
git-stash.sh
@@ -6,6 +6,7 @@ USAGE='[ | list | show | apply | clear]'
|
||||
SUBDIRECTORY_OK=Yes
|
||||
. git-sh-setup
|
||||
require_work_tree
|
||||
cd_to_toplevel
|
||||
|
||||
TMP="$GIT_DIR/.git-stash.$$"
|
||||
trap 'rm -f "$TMP-*"' 0
|
||||
@@ -18,9 +19,10 @@ no_changes () {
|
||||
}
|
||||
|
||||
clear_stash () {
|
||||
logfile="$GIT_DIR/logs/$ref_stash" &&
|
||||
mkdir -p "$(dirname "$logfile")" &&
|
||||
: >"$logfile"
|
||||
if current=$(git rev-parse --verify $ref_stash 2>/dev/null)
|
||||
then
|
||||
git update-ref -d refs/stash $current
|
||||
fi
|
||||
}
|
||||
|
||||
save_stash () {
|
||||
@@ -34,6 +36,9 @@ save_stash () {
|
||||
test -f "$GIT_DIR/logs/$ref_stash" ||
|
||||
clear_stash || die "Cannot initialize stash"
|
||||
|
||||
# Make sure the reflog for stash is kept.
|
||||
: >>"$GIT_DIR/logs/$ref_stash"
|
||||
|
||||
# state of the base commit
|
||||
if b_commit=$(git rev-parse --verify HEAD)
|
||||
then
|
||||
@@ -123,19 +128,24 @@ apply_stash () {
|
||||
c_tree=$(git write-tree) ||
|
||||
die 'Cannot apply a stash in the middle of a merge'
|
||||
|
||||
# stash records the work tree, and is a merge between the
|
||||
# base commit (first parent) and the index tree (second parent).
|
||||
s=$(git rev-parse --revs-only --no-flags --default $ref_stash "$@") &&
|
||||
w_tree=$(git rev-parse --verify "$s:") &&
|
||||
b_tree=$(git rev-parse --verify "$s^:") ||
|
||||
b_tree=$(git rev-parse --verify "$s^1:") &&
|
||||
i_tree=$(git rev-parse --verify "$s^2:") ||
|
||||
die "$*: no valid stashed state found"
|
||||
|
||||
test -z "$unstash_index" || {
|
||||
unstashed_index_tree=
|
||||
if test -n "$unstash_index" && test "$b_tree" != "$i_tree"
|
||||
then
|
||||
git diff --binary $s^2^..$s^2 | git apply --cached
|
||||
test $? -ne 0 &&
|
||||
die 'Conflicts in index. Try without --index.'
|
||||
unstashed_index_tree=$(git-write-tree) ||
|
||||
die 'Could not save index tree'
|
||||
git reset
|
||||
}
|
||||
fi
|
||||
|
||||
eval "
|
||||
GITHEAD_$w_tree='Stashed changes' &&
|
||||
@@ -147,18 +157,25 @@ apply_stash () {
|
||||
if git-merge-recursive $b_tree -- $c_tree $w_tree
|
||||
then
|
||||
# No conflict
|
||||
a="$TMP-added" &&
|
||||
git diff --cached --name-only --diff-filter=A $c_tree >"$a" &&
|
||||
git read-tree --reset $c_tree &&
|
||||
git update-index --add --stdin <"$a" ||
|
||||
die "Cannot unstage modified files"
|
||||
git-status
|
||||
rm -f "$a"
|
||||
test -z "$unstash_index" || git read-tree $unstashed_index_tree
|
||||
if test -n "$unstashed_index_tree"
|
||||
then
|
||||
git read-tree "$unstashed_index_tree"
|
||||
else
|
||||
a="$TMP-added" &&
|
||||
git diff --cached --name-only --diff-filter=A $c_tree >"$a" &&
|
||||
git read-tree --reset $c_tree &&
|
||||
git update-index --add --stdin <"$a" ||
|
||||
die "Cannot unstage modified files"
|
||||
rm -f "$a"
|
||||
fi
|
||||
git status || :
|
||||
else
|
||||
# Merge conflict; keep the exit status from merge-recursive
|
||||
status=$?
|
||||
test -z "$unstash_index" || echo 'Index was not unstashed.' >&2
|
||||
if test -n "$unstash_index"
|
||||
then
|
||||
echo >&2 'Index was not unstashed.'
|
||||
fi
|
||||
exit $status
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -46,8 +46,11 @@ get_repo_base() {
|
||||
#
|
||||
module_name()
|
||||
{
|
||||
name=$(GIT_CONFIG=.gitmodules git config --get-regexp '^submodule\..*\.path$' "$1" |
|
||||
sed -nre 's/^submodule\.(.+)\.path .+$/\1/p')
|
||||
# Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
|
||||
re=$(printf '%s' "$1" | sed -e 's/\([^a-zA-Z0-9_]\)/\\\1/g')
|
||||
name=$( GIT_CONFIG=.gitmodules \
|
||||
git config --get-regexp '^submodule\..*\.path$' |
|
||||
sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
|
||||
test -z "$name" &&
|
||||
die "No submodule mapping found in .gitmodules for path '$path'"
|
||||
echo "$name"
|
||||
@@ -233,7 +236,6 @@ modules_list()
|
||||
say "-$sha1 $path"
|
||||
continue;
|
||||
fi
|
||||
revname=$(unset GIT_DIR && cd "$path" && git describe --tags $sha1)
|
||||
set_name_rev "$path" "$sha1"
|
||||
if git diff-files --quiet -- "$path"
|
||||
then
|
||||
|
||||
@@ -740,7 +740,7 @@ sub load_authors {
|
||||
my $log = $cmd eq 'log';
|
||||
while (<$authors>) {
|
||||
chomp;
|
||||
next unless /^(\S+?|\(no author\))\s*=\s*(.+?)\s*<(.+)>\s*$/;
|
||||
next unless /^(.+?|\(no author\))\s*=\s*(.+?)\s*<(.+)>\s*$/;
|
||||
my ($user, $name, $email) = ($1, $2, $3);
|
||||
if ($log) {
|
||||
$Git::SVN::Log::rusers{"$name <$email>"} = $user;
|
||||
@@ -2724,6 +2724,9 @@ sub repo_path {
|
||||
|
||||
sub url_path {
|
||||
my ($self, $path) = @_;
|
||||
if ($self->{url} =~ m#^https?://#) {
|
||||
$path =~ s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
|
||||
}
|
||||
$self->{url} . '/' . $self->repo_path($path);
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ if [ "$annotate" ]; then
|
||||
( echo "#"
|
||||
echo "# Write a tag message"
|
||||
echo "#" ) > "$GIT_DIR"/TAG_EDITMSG
|
||||
${VISUAL:-${EDITOR:-vi}} "$GIT_DIR"/TAG_EDITMSG || exit
|
||||
git_editor "$GIT_DIR"/TAG_EDITMSG || exit
|
||||
else
|
||||
printf '%s\n' "$message" >"$GIT_DIR"/TAG_EDITMSG
|
||||
fi
|
||||
|
||||
364
gitk
364
gitk
@@ -101,7 +101,7 @@ proc start_rev_list {view} {
|
||||
set commfd($view) $fd
|
||||
set leftover($view) {}
|
||||
set lookingforhead $showlocalchanges
|
||||
fconfigure $fd -blocking 0 -translation lf
|
||||
fconfigure $fd -blocking 0 -translation lf -eofchar {}
|
||||
if {$tclencoding != {}} {
|
||||
fconfigure $fd -encoding $tclencoding
|
||||
}
|
||||
@@ -139,6 +139,10 @@ proc getcommitlines {fd view} {
|
||||
global vparentlist vdisporder vcmitlisted
|
||||
|
||||
set stuff [read $fd 500000]
|
||||
# git log doesn't terminate the last commit with a null...
|
||||
if {$stuff == {} && $leftover($view) ne {} && [eof $fd]} {
|
||||
set stuff "\0"
|
||||
}
|
||||
if {$stuff == {}} {
|
||||
if {![eof $fd]} {
|
||||
return 1
|
||||
@@ -262,11 +266,11 @@ proc chewcommits {view} {
|
||||
set tlimit [expr {[clock clicks -milliseconds] + 50}]
|
||||
set more [layoutmore $tlimit $allread]
|
||||
if {$allread && !$more} {
|
||||
global displayorder nullid commitidx phase
|
||||
global displayorder commitidx phase
|
||||
global numcommits startmsecs
|
||||
|
||||
if {[info exists pending_select]} {
|
||||
set row [expr {[lindex $displayorder 0] eq $nullid}]
|
||||
set row [first_real_row]
|
||||
selectline $row 1
|
||||
}
|
||||
if {$commitidx($curview) > 0} {
|
||||
@@ -437,6 +441,19 @@ proc readrefs {} {
|
||||
}
|
||||
}
|
||||
|
||||
# skip over fake commits
|
||||
proc first_real_row {} {
|
||||
global nullid nullid2 displayorder numcommits
|
||||
|
||||
for {set row 0} {$row < $numcommits} {incr row} {
|
||||
set id [lindex $displayorder $row]
|
||||
if {$id ne $nullid && $id ne $nullid2} {
|
||||
break
|
||||
}
|
||||
}
|
||||
return $row
|
||||
}
|
||||
|
||||
# update things for a head moved to a child of its previous location
|
||||
proc movehead {id name} {
|
||||
global headids idheads
|
||||
@@ -796,6 +813,12 @@ proc makewindow {} {
|
||||
wm geometry . "$geometry(main)"
|
||||
}
|
||||
|
||||
if {[tk windowingsystem] eq {aqua}} {
|
||||
set M1B M1
|
||||
} else {
|
||||
set M1B Control
|
||||
}
|
||||
|
||||
bind .pwbottom <Configure> {resizecdetpanes %W %w}
|
||||
pack .ctop -fill both -expand 1
|
||||
bindall <1> {selcanvline %W %x %y}
|
||||
@@ -814,12 +837,12 @@ proc makewindow {} {
|
||||
bindkey <Key-Left> "goback"
|
||||
bind . <Key-Prior> "selnextpage -1"
|
||||
bind . <Key-Next> "selnextpage 1"
|
||||
bind . <Control-Home> "allcanvs yview moveto 0.0"
|
||||
bind . <Control-End> "allcanvs yview moveto 1.0"
|
||||
bind . <Control-Key-Up> "allcanvs yview scroll -1 units"
|
||||
bind . <Control-Key-Down> "allcanvs yview scroll 1 units"
|
||||
bind . <Control-Key-Prior> "allcanvs yview scroll -1 pages"
|
||||
bind . <Control-Key-Next> "allcanvs yview scroll 1 pages"
|
||||
bind . <$M1B-Home> "allcanvs yview moveto 0.0"
|
||||
bind . <$M1B-End> "allcanvs yview moveto 1.0"
|
||||
bind . <$M1B-Key-Up> "allcanvs yview scroll -1 units"
|
||||
bind . <$M1B-Key-Down> "allcanvs yview scroll 1 units"
|
||||
bind . <$M1B-Key-Prior> "allcanvs yview scroll -1 pages"
|
||||
bind . <$M1B-Key-Next> "allcanvs yview scroll 1 pages"
|
||||
bindkey <Key-Delete> "$ctext yview scroll -1 pages"
|
||||
bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
|
||||
bindkey <Key-space> "$ctext yview scroll 1 pages"
|
||||
@@ -839,15 +862,15 @@ proc makewindow {} {
|
||||
bindkey ? findprev
|
||||
bindkey f nextfile
|
||||
bindkey <F5> updatecommits
|
||||
bind . <Control-q> doquit
|
||||
bind . <Control-f> dofind
|
||||
bind . <Control-g> {findnext 0}
|
||||
bind . <Control-r> dosearchback
|
||||
bind . <Control-s> dosearch
|
||||
bind . <Control-equal> {incrfont 1}
|
||||
bind . <Control-KP_Add> {incrfont 1}
|
||||
bind . <Control-minus> {incrfont -1}
|
||||
bind . <Control-KP_Subtract> {incrfont -1}
|
||||
bind . <$M1B-q> doquit
|
||||
bind . <$M1B-f> dofind
|
||||
bind . <$M1B-g> {findnext 0}
|
||||
bind . <$M1B-r> dosearchback
|
||||
bind . <$M1B-s> dosearch
|
||||
bind . <$M1B-equal> {incrfont 1}
|
||||
bind . <$M1B-KP_Add> {incrfont 1}
|
||||
bind . <$M1B-minus> {incrfont -1}
|
||||
bind . <$M1B-KP_Subtract> {incrfont -1}
|
||||
wm protocol . WM_DELETE_WINDOW doquit
|
||||
bind . <Button-1> "click %W"
|
||||
bind $fstring <Key-Return> dofind
|
||||
@@ -1089,12 +1112,17 @@ proc keys {} {
|
||||
raise $w
|
||||
return
|
||||
}
|
||||
if {[tk windowingsystem] eq {aqua}} {
|
||||
set M1T Cmd
|
||||
} else {
|
||||
set M1T Ctrl
|
||||
}
|
||||
toplevel $w
|
||||
wm title $w "Gitk key bindings"
|
||||
message $w.m -text {
|
||||
message $w.m -text "
|
||||
Gitk key bindings:
|
||||
|
||||
<Ctrl-Q> Quit
|
||||
<$M1T-Q> Quit
|
||||
<Home> Move to first commit
|
||||
<End> Move to last commit
|
||||
<Up>, p, i Move up one commit
|
||||
@@ -1103,12 +1131,12 @@ Gitk key bindings:
|
||||
<Right>, x, l Go forward in history list
|
||||
<PageUp> Move up one page in commit list
|
||||
<PageDown> Move down one page in commit list
|
||||
<Ctrl-Home> Scroll to top of commit list
|
||||
<Ctrl-End> Scroll to bottom of commit list
|
||||
<Ctrl-Up> Scroll commit list up one line
|
||||
<Ctrl-Down> Scroll commit list down one line
|
||||
<Ctrl-PageUp> Scroll commit list up one page
|
||||
<Ctrl-PageDown> Scroll commit list down one page
|
||||
<$M1T-Home> Scroll to top of commit list
|
||||
<$M1T-End> Scroll to bottom of commit list
|
||||
<$M1T-Up> Scroll commit list up one line
|
||||
<$M1T-Down> Scroll commit list down one line
|
||||
<$M1T-PageUp> Scroll commit list up one page
|
||||
<$M1T-PageDown> Scroll commit list down one page
|
||||
<Shift-Up> Move to previous highlighted line
|
||||
<Shift-Down> Move to next highlighted line
|
||||
<Delete>, b Scroll diff view up one page
|
||||
@@ -1116,20 +1144,20 @@ Gitk key bindings:
|
||||
<Space> Scroll diff view down one page
|
||||
u Scroll diff view up 18 lines
|
||||
d Scroll diff view down 18 lines
|
||||
<Ctrl-F> Find
|
||||
<Ctrl-G> Move to next find hit
|
||||
<$M1T-F> Find
|
||||
<$M1T-G> Move to next find hit
|
||||
<Return> Move to next find hit
|
||||
/ Move to next find hit, or redo find
|
||||
? Move to previous find hit
|
||||
f Scroll diff view to next file
|
||||
<Ctrl-S> Search for next hit in diff view
|
||||
<Ctrl-R> Search for previous hit in diff view
|
||||
<Ctrl-KP+> Increase font size
|
||||
<Ctrl-plus> Increase font size
|
||||
<Ctrl-KP-> Decrease font size
|
||||
<Ctrl-minus> Decrease font size
|
||||
<$M1T-S> Search for next hit in diff view
|
||||
<$M1T-R> Search for previous hit in diff view
|
||||
<$M1T-KP+> Increase font size
|
||||
<$M1T-plus> Increase font size
|
||||
<$M1T-KP-> Decrease font size
|
||||
<$M1T-minus> Decrease font size
|
||||
<F5> Update
|
||||
} \
|
||||
" \
|
||||
-justify left -bg white -border 2 -relief groove
|
||||
pack $w.m -side top -fill both -padx 2 -pady 2
|
||||
$w.m configure -font $uifont
|
||||
@@ -1872,7 +1900,7 @@ proc showview {n} {
|
||||
} elseif {$selid ne {}} {
|
||||
set pending_select $selid
|
||||
} else {
|
||||
set row [expr {[lindex $displayorder 0] eq $nullid}]
|
||||
set row [first_real_row]
|
||||
if {$row < $numcommits} {
|
||||
selectline $row 0
|
||||
} else {
|
||||
@@ -2134,7 +2162,7 @@ proc readfhighlight {} {
|
||||
|
||||
proc find_change {name ix op} {
|
||||
global nhighlights mainfont boldnamerows
|
||||
global findstring findpattern findtype markingmatches
|
||||
global findstring findpattern findtype
|
||||
|
||||
# delete previous highlights, if any
|
||||
foreach row $boldnamerows {
|
||||
@@ -2149,7 +2177,6 @@ proc find_change {name ix op} {
|
||||
$findstring]
|
||||
set findpattern "*$e*"
|
||||
}
|
||||
set markingmatches [expr {$findstring ne {}}]
|
||||
drawvisible
|
||||
}
|
||||
|
||||
@@ -2195,26 +2222,32 @@ proc askfindhighlight {row id} {
|
||||
}
|
||||
}
|
||||
if {$markingmatches} {
|
||||
markrowmatches $row [lindex $info 0] [lindex $info 1]
|
||||
markrowmatches $row $id
|
||||
}
|
||||
}
|
||||
set nhighlights($row) $isbold
|
||||
}
|
||||
|
||||
proc markrowmatches {row headline author} {
|
||||
global canv canv2 linehtag linentag
|
||||
proc markrowmatches {row id} {
|
||||
global canv canv2 linehtag linentag commitinfo findloc
|
||||
|
||||
set headline [lindex $commitinfo($id) 0]
|
||||
set author [lindex $commitinfo($id) 1]
|
||||
$canv delete match$row
|
||||
$canv2 delete match$row
|
||||
set m [findmatches $headline]
|
||||
if {$m ne {}} {
|
||||
markmatches $canv $row $headline $linehtag($row) $m \
|
||||
[$canv itemcget $linehtag($row) -font]
|
||||
if {$findloc eq "All fields" || $findloc eq "Headline"} {
|
||||
set m [findmatches $headline]
|
||||
if {$m ne {}} {
|
||||
markmatches $canv $row $headline $linehtag($row) $m \
|
||||
[$canv itemcget $linehtag($row) -font] $row
|
||||
}
|
||||
}
|
||||
set m [findmatches $author]
|
||||
if {$m ne {}} {
|
||||
markmatches $canv2 $row $author $linentag($row) $m \
|
||||
[$canv2 itemcget $linentag($row) -font]
|
||||
if {$findloc eq "All fields" || $findloc eq "Author"} {
|
||||
set m [findmatches $author]
|
||||
if {$m ne {}} {
|
||||
markmatches $canv2 $row $author $linentag($row) $m \
|
||||
[$canv2 itemcget $linentag($row) -font] $row
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2644,7 +2677,7 @@ proc layoutmore {tmax allread} {
|
||||
|
||||
proc showstuff {canshow last} {
|
||||
global numcommits commitrow pending_select selectedline curview
|
||||
global lookingforhead mainheadid displayorder nullid selectfirst
|
||||
global lookingforhead mainheadid displayorder selectfirst
|
||||
global lastscrollset
|
||||
|
||||
if {$numcommits == 0} {
|
||||
@@ -2677,7 +2710,7 @@ proc showstuff {canshow last} {
|
||||
if {[info exists selectedline] || [info exists pending_select]} {
|
||||
set selectfirst 0
|
||||
} else {
|
||||
set l [expr {[lindex $displayorder 0] eq $nullid}]
|
||||
set l [first_real_row]
|
||||
selectline $l 1
|
||||
set selectfirst 0
|
||||
}
|
||||
@@ -2701,48 +2734,93 @@ proc doshowlocalchanges {} {
|
||||
}
|
||||
|
||||
proc dohidelocalchanges {} {
|
||||
global lookingforhead localrow lserial
|
||||
global lookingforhead localfrow localirow lserial
|
||||
|
||||
set lookingforhead 0
|
||||
if {$localrow >= 0} {
|
||||
removerow $localrow
|
||||
set localrow -1
|
||||
if {$localfrow >= 0} {
|
||||
removerow $localfrow
|
||||
set localfrow -1
|
||||
if {$localirow > 0} {
|
||||
incr localirow -1
|
||||
}
|
||||
}
|
||||
if {$localirow >= 0} {
|
||||
removerow $localirow
|
||||
set localirow -1
|
||||
}
|
||||
incr lserial
|
||||
}
|
||||
|
||||
# spawn off a process to do git diff-index HEAD
|
||||
# spawn off a process to do git diff-index --cached HEAD
|
||||
proc dodiffindex {} {
|
||||
global localrow lserial
|
||||
global localirow localfrow lserial
|
||||
|
||||
incr lserial
|
||||
set localrow -1
|
||||
set fd [open "|git diff-index HEAD" r]
|
||||
set localfrow -1
|
||||
set localirow -1
|
||||
set fd [open "|git diff-index --cached HEAD" r]
|
||||
fconfigure $fd -blocking 0
|
||||
filerun $fd [list readdiffindex $fd $lserial]
|
||||
}
|
||||
|
||||
proc readdiffindex {fd serial} {
|
||||
global localrow commitrow mainheadid nullid curview
|
||||
global localirow commitrow mainheadid nullid2 curview
|
||||
global commitinfo commitdata lserial
|
||||
|
||||
set isdiff 1
|
||||
if {[gets $fd line] < 0} {
|
||||
if {[eof $fd]} {
|
||||
close $fd
|
||||
return 0
|
||||
if {![eof $fd]} {
|
||||
return 1
|
||||
}
|
||||
return 1
|
||||
set isdiff 0
|
||||
}
|
||||
# we only need to see one line and we don't really care what it says...
|
||||
close $fd
|
||||
|
||||
if {$serial == $lserial && $localrow == -1} {
|
||||
# now see if there are any local changes not checked in to the index
|
||||
if {$serial == $lserial} {
|
||||
set fd [open "|git diff-files" r]
|
||||
fconfigure $fd -blocking 0
|
||||
filerun $fd [list readdifffiles $fd $serial]
|
||||
}
|
||||
|
||||
if {$isdiff && $serial == $lserial && $localirow == -1} {
|
||||
# add the line for the changes in the index to the graph
|
||||
set localirow $commitrow($curview,$mainheadid)
|
||||
set hl "Local changes checked in to index but not committed"
|
||||
set commitinfo($nullid2) [list $hl {} {} {} {} " $hl\n"]
|
||||
set commitdata($nullid2) "\n $hl\n"
|
||||
insertrow $localirow $nullid2
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
proc readdifffiles {fd serial} {
|
||||
global localirow localfrow commitrow mainheadid nullid curview
|
||||
global commitinfo commitdata lserial
|
||||
|
||||
set isdiff 1
|
||||
if {[gets $fd line] < 0} {
|
||||
if {![eof $fd]} {
|
||||
return 1
|
||||
}
|
||||
set isdiff 0
|
||||
}
|
||||
# we only need to see one line and we don't really care what it says...
|
||||
close $fd
|
||||
|
||||
if {$isdiff && $serial == $lserial && $localfrow == -1} {
|
||||
# add the line for the local diff to the graph
|
||||
set localrow $commitrow($curview,$mainheadid)
|
||||
set hl "Local uncommitted changes"
|
||||
if {$localirow >= 0} {
|
||||
set localfrow $localirow
|
||||
incr localirow
|
||||
} else {
|
||||
set localfrow $commitrow($curview,$mainheadid)
|
||||
}
|
||||
set hl "Local uncommitted changes, not checked in to index"
|
||||
set commitinfo($nullid) [list $hl {} {} {} {} " $hl\n"]
|
||||
set commitdata($nullid) "\n $hl\n"
|
||||
insertrow $localrow $nullid
|
||||
insertrow $localfrow $nullid
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -3338,13 +3416,15 @@ proc drawcmittext {id row col} {
|
||||
global linespc canv canv2 canv3 canvy0 fgcolor curview
|
||||
global commitlisted commitinfo rowidlist parentlist
|
||||
global rowtextx idpos idtags idheads idotherrefs
|
||||
global linehtag linentag linedtag markingmatches
|
||||
global mainfont canvxmax boldrows boldnamerows fgcolor nullid
|
||||
global linehtag linentag linedtag
|
||||
global mainfont canvxmax boldrows boldnamerows fgcolor nullid nullid2
|
||||
|
||||
# listed is 0 for boundary, 1 for normal, 2 for left, 3 for right
|
||||
set listed [lindex $commitlisted $row]
|
||||
if {$id eq $nullid} {
|
||||
set ofill red
|
||||
} elseif {$id eq $nullid2} {
|
||||
set ofill green
|
||||
} else {
|
||||
set ofill [expr {$listed != 0? "blue": "white"}]
|
||||
}
|
||||
@@ -3413,9 +3493,6 @@ proc drawcmittext {id row col} {
|
||||
set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
|
||||
-text $date -font $mainfont -tags text]
|
||||
set xr [expr {$xt + [font measure $mainfont $headline]}]
|
||||
if {$markingmatches} {
|
||||
markrowmatches $row $headline $name
|
||||
}
|
||||
if {$xr > $canvxmax} {
|
||||
set canvxmax $xr
|
||||
setcanvscroll
|
||||
@@ -3424,7 +3501,7 @@ proc drawcmittext {id row col} {
|
||||
|
||||
proc drawcmitrow {row} {
|
||||
global displayorder rowidlist
|
||||
global iddrawn
|
||||
global iddrawn markingmatches
|
||||
global commitinfo parentlist numcommits
|
||||
global filehighlight fhighlights findstring nhighlights
|
||||
global hlview vhighlights
|
||||
@@ -3445,18 +3522,22 @@ proc drawcmitrow {row} {
|
||||
if {$highlight_related ne "None" && ![info exists rhighlights($row)]} {
|
||||
askrelhighlight $row $id
|
||||
}
|
||||
if {[info exists iddrawn($id)]} return
|
||||
set col [lsearch -exact [lindex $rowidlist $row] $id]
|
||||
if {$col < 0} {
|
||||
puts "oops, row $row id $id not in list"
|
||||
return
|
||||
if {![info exists iddrawn($id)]} {
|
||||
set col [lsearch -exact [lindex $rowidlist $row] $id]
|
||||
if {$col < 0} {
|
||||
puts "oops, row $row id $id not in list"
|
||||
return
|
||||
}
|
||||
if {![info exists commitinfo($id)]} {
|
||||
getcommit $id
|
||||
}
|
||||
assigncolor $id
|
||||
drawcmittext $id $row $col
|
||||
set iddrawn($id) 1
|
||||
}
|
||||
if {![info exists commitinfo($id)]} {
|
||||
getcommit $id
|
||||
if {$markingmatches} {
|
||||
markrowmatches $row $id
|
||||
}
|
||||
assigncolor $id
|
||||
drawcmittext $id $row $col
|
||||
set iddrawn($id) 1
|
||||
}
|
||||
|
||||
proc drawcommits {row {endrow {}}} {
|
||||
@@ -3974,7 +4055,6 @@ proc dofind {{rev 0}} {
|
||||
if {!$rev} {
|
||||
run findmore
|
||||
} else {
|
||||
set findcurline $findstartline
|
||||
if {$findcurline == 0} {
|
||||
set findcurline $numcommits
|
||||
}
|
||||
@@ -4009,7 +4089,7 @@ proc findprev {} {
|
||||
|
||||
proc findmore {} {
|
||||
global commitdata commitinfo numcommits findstring findpattern findloc
|
||||
global findstartline findcurline markingmatches displayorder
|
||||
global findstartline findcurline displayorder
|
||||
|
||||
set fldtypes {Headline Author Date Committer CDate Comments}
|
||||
set l [expr {$findcurline + 1}]
|
||||
@@ -4027,6 +4107,8 @@ proc findmore {} {
|
||||
set last 0
|
||||
for {} {$l < $lim} {incr l} {
|
||||
set id [lindex $displayorder $l]
|
||||
# shouldn't happen unless git log doesn't give all the commits...
|
||||
if {![info exists commitdata($id)]} continue
|
||||
if {![doesmatch $commitdata($id)]} continue
|
||||
if {![info exists commitinfo($id)]} {
|
||||
getcommit $id
|
||||
@@ -4035,7 +4117,6 @@ proc findmore {} {
|
||||
foreach f $info ty $fldtypes {
|
||||
if {($findloc eq "All fields" || $findloc eq $ty) &&
|
||||
[doesmatch $f]} {
|
||||
set markingmatches 1
|
||||
findselectline $l
|
||||
notbusy finding
|
||||
return 0
|
||||
@@ -4054,7 +4135,7 @@ proc findmore {} {
|
||||
|
||||
proc findmorerev {} {
|
||||
global commitdata commitinfo numcommits findstring findpattern findloc
|
||||
global findstartline findcurline markingmatches displayorder
|
||||
global findstartline findcurline displayorder
|
||||
|
||||
set fldtypes {Headline Author Date Committer CDate Comments}
|
||||
set l $findcurline
|
||||
@@ -4081,7 +4162,6 @@ proc findmorerev {} {
|
||||
foreach f $info ty $fldtypes {
|
||||
if {($findloc eq "All fields" || $findloc eq $ty) &&
|
||||
[doesmatch $f]} {
|
||||
set markingmatches 1
|
||||
findselectline $l
|
||||
notbusy finding
|
||||
return 0
|
||||
@@ -4099,7 +4179,10 @@ proc findmorerev {} {
|
||||
}
|
||||
|
||||
proc findselectline {l} {
|
||||
global findloc commentend ctext
|
||||
global findloc commentend ctext findcurline markingmatches
|
||||
|
||||
set markingmatches 1
|
||||
set findcurline $l
|
||||
selectline $l 1
|
||||
if {$findloc == "All fields" || $findloc == "Comments"} {
|
||||
# highlight the matches in the comments
|
||||
@@ -4111,10 +4194,13 @@ proc findselectline {l} {
|
||||
$ctext tag add found "1.0 + $start c" "1.0 + $end c"
|
||||
}
|
||||
}
|
||||
drawvisible
|
||||
}
|
||||
|
||||
# mark the bits of a headline or author that match a find string
|
||||
proc markmatches {canv l str tag matches font} {
|
||||
proc markmatches {canv l str tag matches font row} {
|
||||
global selectedline
|
||||
|
||||
set bbox [$canv bbox $tag]
|
||||
set x0 [lindex $bbox 0]
|
||||
set y0 [lindex $bbox 1]
|
||||
@@ -4129,6 +4215,9 @@ proc markmatches {canv l str tag matches font} {
|
||||
[expr {$x0+$xlen+2}] $y1 \
|
||||
-outline {} -tags [list match$l matches] -fill yellow]
|
||||
$canv lower $t
|
||||
if {[info exists selectedline] && $row == $selectedline} {
|
||||
$canv raise $t secsel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4583,16 +4672,19 @@ proc goforw {} {
|
||||
}
|
||||
|
||||
proc gettree {id} {
|
||||
global treefilelist treeidlist diffids diffmergeid treepending nullid
|
||||
global treefilelist treeidlist diffids diffmergeid treepending
|
||||
global nullid nullid2
|
||||
|
||||
set diffids $id
|
||||
catch {unset diffmergeid}
|
||||
if {![info exists treefilelist($id)]} {
|
||||
if {![info exists treepending]} {
|
||||
if {$id ne $nullid} {
|
||||
set cmd [concat | git ls-tree -r $id]
|
||||
if {$id eq $nullid} {
|
||||
set cmd [list | git ls-files]
|
||||
} elseif {$id eq $nullid2} {
|
||||
set cmd [list | git ls-files --stage -t]
|
||||
} else {
|
||||
set cmd [concat | git ls-files]
|
||||
set cmd [list | git ls-tree -r $id]
|
||||
}
|
||||
if {[catch {set gtf [open $cmd r]}]} {
|
||||
return
|
||||
@@ -4609,12 +4701,14 @@ proc gettree {id} {
|
||||
}
|
||||
|
||||
proc gettreeline {gtf id} {
|
||||
global treefilelist treeidlist treepending cmitmode diffids nullid
|
||||
global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
|
||||
|
||||
set nl 0
|
||||
while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
|
||||
if {$diffids ne $nullid} {
|
||||
if {[lindex $line 1] ne "blob"} continue
|
||||
if {$diffids eq $nullid} {
|
||||
set fname $line
|
||||
} else {
|
||||
if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
|
||||
set i [string first "\t" $line]
|
||||
if {$i < 0} continue
|
||||
set sha1 [lindex $line 2]
|
||||
@@ -4623,8 +4717,6 @@ proc gettreeline {gtf id} {
|
||||
set fname [lindex $fname 0]
|
||||
}
|
||||
lappend treeidlist($id) $sha1
|
||||
} else {
|
||||
set fname $line
|
||||
}
|
||||
lappend treefilelist($id) $fname
|
||||
}
|
||||
@@ -4646,7 +4738,7 @@ proc gettreeline {gtf id} {
|
||||
}
|
||||
|
||||
proc showfile {f} {
|
||||
global treefilelist treeidlist diffids nullid
|
||||
global treefilelist treeidlist diffids nullid nullid2
|
||||
global ctext commentend
|
||||
|
||||
set i [lsearch -exact $treefilelist($diffids) $f]
|
||||
@@ -4654,15 +4746,15 @@ proc showfile {f} {
|
||||
puts "oops, $f not in list for id $diffids"
|
||||
return
|
||||
}
|
||||
if {$diffids ne $nullid} {
|
||||
set blob [lindex $treeidlist($diffids) $i]
|
||||
if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
|
||||
puts "oops, error reading blob $blob: $err"
|
||||
if {$diffids eq $nullid} {
|
||||
if {[catch {set bf [open $f r]} err]} {
|
||||
puts "oops, can't read $f: $err"
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if {[catch {set bf [open $f r]} err]} {
|
||||
puts "oops, can't read $f: $err"
|
||||
set blob [lindex $treeidlist($diffids) $i]
|
||||
if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
|
||||
puts "oops, error reading blob $blob: $err"
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -4790,11 +4882,13 @@ proc getmergediffline {mdf id np} {
|
||||
}
|
||||
|
||||
proc startdiff {ids} {
|
||||
global treediffs diffids treepending diffmergeid nullid
|
||||
global treediffs diffids treepending diffmergeid nullid nullid2
|
||||
|
||||
set diffids $ids
|
||||
catch {unset diffmergeid}
|
||||
if {![info exists treediffs($ids)] || [lsearch -exact $ids $nullid] >= 0} {
|
||||
if {![info exists treediffs($ids)] ||
|
||||
[lsearch -exact $ids $nullid] >= 0 ||
|
||||
[lsearch -exact $ids $nullid2] >= 0} {
|
||||
if {![info exists treepending]} {
|
||||
gettreediffs $ids
|
||||
}
|
||||
@@ -4810,22 +4904,41 @@ proc addtocflist {ids} {
|
||||
}
|
||||
|
||||
proc diffcmd {ids flags} {
|
||||
global nullid
|
||||
global nullid nullid2
|
||||
|
||||
set i [lsearch -exact $ids $nullid]
|
||||
set j [lsearch -exact $ids $nullid2]
|
||||
if {$i >= 0} {
|
||||
set cmd [concat | git diff-index $flags]
|
||||
if {[llength $ids] > 1} {
|
||||
if {[llength $ids] > 1 && $j < 0} {
|
||||
# comparing working directory with some specific revision
|
||||
set cmd [concat | git diff-index $flags]
|
||||
if {$i == 0} {
|
||||
lappend cmd -R [lindex $ids 1]
|
||||
} else {
|
||||
lappend cmd [lindex $ids 0]
|
||||
}
|
||||
} else {
|
||||
# comparing working directory with index
|
||||
set cmd [concat | git diff-files $flags]
|
||||
if {$j == 1} {
|
||||
lappend cmd -R
|
||||
}
|
||||
}
|
||||
} elseif {$j >= 0} {
|
||||
set cmd [concat | git diff-index --cached $flags]
|
||||
if {[llength $ids] > 1} {
|
||||
# comparing index with specific revision
|
||||
if {$i == 0} {
|
||||
lappend cmd -R [lindex $ids 1]
|
||||
} else {
|
||||
lappend cmd [lindex $ids 0]
|
||||
}
|
||||
} else {
|
||||
# comparing index with HEAD
|
||||
lappend cmd HEAD
|
||||
}
|
||||
} else {
|
||||
set cmd [concat | git diff-tree --no-commit-id -r $flags $ids]
|
||||
set cmd [concat | git diff-tree -r $flags $ids]
|
||||
}
|
||||
return $cmd
|
||||
}
|
||||
@@ -4835,7 +4948,7 @@ proc gettreediffs {ids} {
|
||||
|
||||
set treepending $ids
|
||||
set treediff {}
|
||||
if {[catch {set gdtf [open [diffcmd $ids {}] r]}]} return
|
||||
if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
|
||||
fconfigure $gdtf -blocking 0
|
||||
filerun $gdtf [list gettreediffline $gdtf $ids]
|
||||
}
|
||||
@@ -4878,7 +4991,7 @@ proc getblobdiffs {ids} {
|
||||
global diffinhdr treediffs
|
||||
|
||||
set env(GIT_DIFF_OPTS) $diffopts
|
||||
if {[catch {set bdf [open [diffcmd $ids {-p -C}] r]} err]} {
|
||||
if {[catch {set bdf [open [diffcmd $ids {-p -C --no-commit-id}] r]} err]} {
|
||||
puts "error getting diffs: $err"
|
||||
return
|
||||
}
|
||||
@@ -5469,7 +5582,7 @@ proc mstime {} {
|
||||
|
||||
proc rowmenu {x y id} {
|
||||
global rowctxmenu commitrow selectedline rowmenuid curview
|
||||
global nullid fakerowmenu mainhead
|
||||
global nullid nullid2 fakerowmenu mainhead
|
||||
|
||||
set rowmenuid $id
|
||||
if {![info exists selectedline]
|
||||
@@ -5478,7 +5591,7 @@ proc rowmenu {x y id} {
|
||||
} else {
|
||||
set state normal
|
||||
}
|
||||
if {$id ne $nullid} {
|
||||
if {$id ne $nullid && $id ne $nullid2} {
|
||||
set menu $rowctxmenu
|
||||
$menu entryconfigure 7 -label "Reset $mainhead branch to here"
|
||||
} else {
|
||||
@@ -5597,18 +5710,12 @@ proc mkpatchrev {} {
|
||||
}
|
||||
|
||||
proc mkpatchgo {} {
|
||||
global patchtop nullid
|
||||
global patchtop nullid nullid2
|
||||
|
||||
set oldid [$patchtop.fromsha1 get]
|
||||
set newid [$patchtop.tosha1 get]
|
||||
set fname [$patchtop.fname get]
|
||||
if {$newid eq $nullid} {
|
||||
set cmd [list git diff-index -p $oldid]
|
||||
} elseif {$oldid eq $nullid} {
|
||||
set cmd [list git diff-index -p -R $newid]
|
||||
} else {
|
||||
set cmd [list git diff-tree -p $oldid $newid]
|
||||
}
|
||||
set cmd [diffcmd [list $oldid $newid] -p]
|
||||
lappend cmd >$fname &
|
||||
if {[catch {eval exec $cmd} err]} {
|
||||
error_popup "Error creating patch: $err"
|
||||
@@ -7523,6 +7630,8 @@ if {$i >= [llength $argv] && $revtreeargs ne {}} {
|
||||
}
|
||||
|
||||
set nullid "0000000000000000000000000000000000000000"
|
||||
set nullid2 "0000000000000000000000000000000000000001"
|
||||
|
||||
|
||||
set runq {}
|
||||
set history {}
|
||||
@@ -7551,10 +7660,13 @@ set stopped 0
|
||||
set stuffsaved 0
|
||||
set patchnum 0
|
||||
set lookingforhead 0
|
||||
set localrow -1
|
||||
set localirow -1
|
||||
set localfrow -1
|
||||
set lserial 0
|
||||
setcoords
|
||||
makewindow
|
||||
# wait for the window to become visible
|
||||
tkwait visibility .
|
||||
wm title . "[file tail $argv0]: [file tail [pwd]]"
|
||||
readrefs
|
||||
|
||||
|
||||
@@ -104,6 +104,59 @@ our $mimetypes_file = undef;
|
||||
# could be even 'utf-8' for the old behavior)
|
||||
our $fallback_encoding = 'latin1';
|
||||
|
||||
# rename detection options for git-diff and git-diff-tree
|
||||
# - default is '-M', with the cost proportional to
|
||||
# (number of removed files) * (number of new files).
|
||||
# - more costly is '-C' (which implies '-M'), with the cost proportional to
|
||||
# (number of changed files + number of removed files) * (number of new files)
|
||||
# - even more costly is '-C', '--find-copies-harder' with cost
|
||||
# (number of files in the original tree) * (number of new files)
|
||||
# - one might want to include '-B' option, e.g. '-B', '-M'
|
||||
our @diff_opts = ('-M'); # taken from git_commit
|
||||
|
||||
# information about snapshot formats that gitweb is capable of serving
|
||||
our %known_snapshot_formats = (
|
||||
# name => {
|
||||
# 'display' => display name,
|
||||
# 'type' => mime type,
|
||||
# 'suffix' => filename suffix,
|
||||
# 'format' => --format for git-archive,
|
||||
# 'compressor' => [compressor command and arguments]
|
||||
# (array reference, optional)}
|
||||
#
|
||||
'tgz' => {
|
||||
'display' => 'tar.gz',
|
||||
'type' => 'application/x-gzip',
|
||||
'suffix' => '.tar.gz',
|
||||
'format' => 'tar',
|
||||
'compressor' => ['gzip']},
|
||||
|
||||
'tbz2' => {
|
||||
'display' => 'tar.bz2',
|
||||
'type' => 'application/x-bzip2',
|
||||
'suffix' => '.tar.bz2',
|
||||
'format' => 'tar',
|
||||
'compressor' => ['bzip2']},
|
||||
|
||||
'zip' => {
|
||||
'display' => 'zip',
|
||||
'type' => 'application/x-zip',
|
||||
'suffix' => '.zip',
|
||||
'format' => 'zip'},
|
||||
);
|
||||
|
||||
# Aliases so we understand old gitweb.snapshot values in repository
|
||||
# configuration.
|
||||
our %known_snapshot_format_aliases = (
|
||||
'gzip' => 'tgz',
|
||||
'bzip2' => 'tbz2',
|
||||
|
||||
# backward compatibility: legacy gitweb config support
|
||||
'x-gzip' => undef, 'gz' => undef,
|
||||
'x-bzip2' => undef, 'bz2' => undef,
|
||||
'x-zip' => undef, '' => undef,
|
||||
);
|
||||
|
||||
# You define site-wide feature defaults here; override them with
|
||||
# $GITWEB_CONFIG as necessary.
|
||||
our %feature = (
|
||||
@@ -134,20 +187,22 @@ our %feature = (
|
||||
'override' => 0,
|
||||
'default' => [0]},
|
||||
|
||||
# Enable the 'snapshot' link, providing a compressed tarball of any
|
||||
# Enable the 'snapshot' link, providing a compressed archive of any
|
||||
# tree. This can potentially generate high traffic if you have large
|
||||
# project.
|
||||
|
||||
# Value is a list of formats defined in %known_snapshot_formats that
|
||||
# you wish to offer.
|
||||
# To disable system wide have in $GITWEB_CONFIG
|
||||
# $feature{'snapshot'}{'default'} = [undef];
|
||||
# $feature{'snapshot'}{'default'} = [];
|
||||
# To have project specific config enable override in $GITWEB_CONFIG
|
||||
# $feature{'snapshot'}{'override'} = 1;
|
||||
# and in project config gitweb.snapshot = none|gzip|bzip2|zip;
|
||||
# and in project config, a comma-separated list of formats or "none"
|
||||
# to disable. Example: gitweb.snapshot = tbz2,zip;
|
||||
'snapshot' => {
|
||||
'sub' => \&feature_snapshot,
|
||||
'override' => 0,
|
||||
# => [content-encoding, suffix, program]
|
||||
'default' => ['x-gzip', 'gz', 'gzip']},
|
||||
'default' => ['tgz']},
|
||||
|
||||
# Enable text search, which will list the commits which match author,
|
||||
# committer or commit text to a given string. Enabled by default.
|
||||
@@ -246,28 +301,15 @@ sub feature_blame {
|
||||
}
|
||||
|
||||
sub feature_snapshot {
|
||||
my ($ctype, $suffix, $command) = @_;
|
||||
my (@fmts) = @_;
|
||||
|
||||
my ($val) = git_get_project_config('snapshot');
|
||||
|
||||
if ($val eq 'gzip') {
|
||||
return ('x-gzip', 'gz', 'gzip');
|
||||
} elsif ($val eq 'bzip2') {
|
||||
return ('x-bzip2', 'bz2', 'bzip2');
|
||||
} elsif ($val eq 'zip') {
|
||||
return ('x-zip', 'zip', '');
|
||||
} elsif ($val eq 'none') {
|
||||
return ();
|
||||
if ($val) {
|
||||
@fmts = ($val eq 'none' ? () : split /\s*[,\s]\s*/, $val);
|
||||
}
|
||||
|
||||
return ($ctype, $suffix, $command);
|
||||
}
|
||||
|
||||
sub gitweb_have_snapshot {
|
||||
my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
|
||||
my $have_snapshot = (defined $ctype && defined $suffix);
|
||||
|
||||
return $have_snapshot;
|
||||
return @fmts;
|
||||
}
|
||||
|
||||
sub feature_grep {
|
||||
@@ -310,15 +352,17 @@ sub check_export_ok {
|
||||
(!$export_ok || -e "$dir/$export_ok"));
|
||||
}
|
||||
|
||||
# rename detection options for git-diff and git-diff-tree
|
||||
# - default is '-M', with the cost proportional to
|
||||
# (number of removed files) * (number of new files).
|
||||
# - more costly is '-C' (or '-C', '-M'), with the cost proportional to
|
||||
# (number of changed files + number of removed files) * (number of new files)
|
||||
# - even more costly is '-C', '--find-copies-harder' with cost
|
||||
# (number of files in the original tree) * (number of new files)
|
||||
# - one might want to include '-B' option, e.g. '-B', '-M'
|
||||
our @diff_opts = ('-M'); # taken from git_commit
|
||||
# process alternate names for backward compatibility
|
||||
# filter out unsupported (unknown) snapshot formats
|
||||
sub filter_snapshot_fmts {
|
||||
my @fmts = @_;
|
||||
|
||||
@fmts = map {
|
||||
exists $known_snapshot_format_aliases{$_} ?
|
||||
$known_snapshot_format_aliases{$_} : $_} @fmts;
|
||||
@fmts = grep(exists $known_snapshot_formats{$_}, @fmts);
|
||||
|
||||
}
|
||||
|
||||
our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
|
||||
do $GITWEB_CONFIG if -e $GITWEB_CONFIG;
|
||||
@@ -392,12 +436,11 @@ my %allowed_options = (
|
||||
|
||||
our @extra_options = $cgi->param('opt');
|
||||
if (defined @extra_options) {
|
||||
foreach(@extra_options)
|
||||
{
|
||||
if (not grep(/^$_$/, keys %allowed_options)) {
|
||||
foreach my $opt (@extra_options) {
|
||||
if (not exists $allowed_options{$opt}) {
|
||||
die_error(undef, "Invalid option parameter");
|
||||
}
|
||||
if (not grep(/^$action$/, @{$allowed_options{$_}})) {
|
||||
if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
|
||||
die_error(undef, "Invalid option parameter for this action");
|
||||
}
|
||||
}
|
||||
@@ -554,7 +597,6 @@ sub href(%) {
|
||||
action => "a",
|
||||
file_name => "f",
|
||||
file_parent => "fp",
|
||||
extra_options => "opt",
|
||||
hash => "h",
|
||||
hash_parent => "hp",
|
||||
hash_base => "hb",
|
||||
@@ -563,6 +605,8 @@ sub href(%) {
|
||||
order => "o",
|
||||
searchtext => "s",
|
||||
searchtype => "st",
|
||||
snapshot_format => "sf",
|
||||
extra_options => "opt",
|
||||
);
|
||||
my %mapping = @mapping;
|
||||
|
||||
@@ -585,7 +629,13 @@ sub href(%) {
|
||||
for (my $i = 0; $i < @mapping; $i += 2) {
|
||||
my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]);
|
||||
if (defined $params{$name}) {
|
||||
push @result, $symbol . "=" . esc_param($params{$name});
|
||||
if (ref($params{$name}) eq "ARRAY") {
|
||||
foreach my $par (@{$params{$name}}) {
|
||||
push @result, $symbol . "=" . esc_param($par);
|
||||
}
|
||||
} else {
|
||||
push @result, $symbol . "=" . esc_param($params{$name});
|
||||
}
|
||||
}
|
||||
}
|
||||
$href .= "?" . join(';', @result) if scalar @result;
|
||||
@@ -845,11 +895,25 @@ sub age_string {
|
||||
return $age_str;
|
||||
}
|
||||
|
||||
use constant {
|
||||
S_IFINVALID => 0030000,
|
||||
S_IFGITLINK => 0160000,
|
||||
};
|
||||
|
||||
# submodule/subproject, a commit object reference
|
||||
sub S_ISGITLINK($) {
|
||||
my $mode = shift;
|
||||
|
||||
return (($mode & S_IFMT) == S_IFGITLINK)
|
||||
}
|
||||
|
||||
# convert file mode in octal to symbolic file mode string
|
||||
sub mode_str {
|
||||
my $mode = oct shift;
|
||||
|
||||
if (S_ISDIR($mode & S_IFMT)) {
|
||||
if (S_ISGITLINK($mode)) {
|
||||
return 'm---------';
|
||||
} elsif (S_ISDIR($mode & S_IFMT)) {
|
||||
return 'drwxr-xr-x';
|
||||
} elsif (S_ISLNK($mode)) {
|
||||
return 'lrwxrwxrwx';
|
||||
@@ -875,7 +939,9 @@ sub file_type {
|
||||
$mode = oct $mode;
|
||||
}
|
||||
|
||||
if (S_ISDIR($mode & S_IFMT)) {
|
||||
if (S_ISGITLINK($mode)) {
|
||||
return "submodule";
|
||||
} elsif (S_ISDIR($mode & S_IFMT)) {
|
||||
return "directory";
|
||||
} elsif (S_ISLNK($mode)) {
|
||||
return "symlink";
|
||||
@@ -896,7 +962,9 @@ sub file_type_long {
|
||||
$mode = oct $mode;
|
||||
}
|
||||
|
||||
if (S_ISDIR($mode & S_IFMT)) {
|
||||
if (S_ISGITLINK($mode)) {
|
||||
return "submodule";
|
||||
} elsif (S_ISDIR($mode & S_IFMT)) {
|
||||
return "directory";
|
||||
} elsif (S_ISLNK($mode)) {
|
||||
return "symlink";
|
||||
@@ -1257,6 +1325,43 @@ sub format_diff_line {
|
||||
return "<div class=\"diff$diff_class\">" . esc_html($line, -nbsp=>1) . "</div>\n";
|
||||
}
|
||||
|
||||
# Generates undef or something like "_snapshot_" or "snapshot (_tbz2_ _zip_)",
|
||||
# linked. Pass the hash of the tree/commit to snapshot.
|
||||
sub format_snapshot_links {
|
||||
my ($hash) = @_;
|
||||
my @snapshot_fmts = gitweb_check_feature('snapshot');
|
||||
@snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
|
||||
my $num_fmts = @snapshot_fmts;
|
||||
if ($num_fmts > 1) {
|
||||
# A parenthesized list of links bearing format names.
|
||||
# e.g. "snapshot (_tar.gz_ _zip_)"
|
||||
return "snapshot (" . join(' ', map
|
||||
$cgi->a({
|
||||
-href => href(
|
||||
action=>"snapshot",
|
||||
hash=>$hash,
|
||||
snapshot_format=>$_
|
||||
)
|
||||
}, $known_snapshot_formats{$_}{'display'})
|
||||
, @snapshot_fmts) . ")";
|
||||
} elsif ($num_fmts == 1) {
|
||||
# A single "snapshot" link whose tooltip bears the format name.
|
||||
# i.e. "_snapshot_"
|
||||
my ($fmt) = @snapshot_fmts;
|
||||
return
|
||||
$cgi->a({
|
||||
-href => href(
|
||||
action=>"snapshot",
|
||||
hash=>$hash,
|
||||
snapshot_format=>$fmt
|
||||
),
|
||||
-title => "in format: $known_snapshot_formats{$fmt}{'display'}"
|
||||
}, "snapshot");
|
||||
} else { # $num_fmts == 0
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## git utility subroutines, invoking git commands
|
||||
|
||||
@@ -2185,9 +2290,17 @@ EOF
|
||||
printf('<link rel="alternate" title="%s log RSS feed" '.
|
||||
'href="%s" type="application/rss+xml" />'."\n",
|
||||
esc_param($project), href(action=>"rss"));
|
||||
printf('<link rel="alternate" title="%s log RSS feed (no merges)" '.
|
||||
'href="%s" type="application/rss+xml" />'."\n",
|
||||
esc_param($project), href(action=>"rss",
|
||||
extra_options=>"--no-merges"));
|
||||
printf('<link rel="alternate" title="%s log Atom feed" '.
|
||||
'href="%s" type="application/atom+xml" />'."\n",
|
||||
esc_param($project), href(action=>"atom"));
|
||||
printf('<link rel="alternate" title="%s log Atom feed (no merges)" '.
|
||||
'href="%s" type="application/atom+xml" />'."\n",
|
||||
esc_param($project), href(action=>"atom",
|
||||
extra_options=>"--no-merges"));
|
||||
} else {
|
||||
printf('<link rel="alternate" title="%s projects list" '.
|
||||
'href="%s" type="text/plain; charset=utf-8"/>'."\n",
|
||||
@@ -2625,6 +2738,20 @@ sub git_print_tree_entry {
|
||||
"history");
|
||||
}
|
||||
print "</td>\n";
|
||||
} else {
|
||||
# unknown object: we can only present history for it
|
||||
# (this includes 'commit' object, i.e. submodule support)
|
||||
print "<td class=\"list\">" .
|
||||
esc_path($t->{'name'}) .
|
||||
"</td>\n";
|
||||
print "<td class=\"link\">";
|
||||
if (defined $hash_base) {
|
||||
print $cgi->a({-href => href(action=>"history",
|
||||
hash_base=>$hash_base,
|
||||
file_name=>"$basedir$t->{'name'}")},
|
||||
"history");
|
||||
}
|
||||
print "</td>\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3321,8 +3448,6 @@ sub git_shortlog_body {
|
||||
# uses global variable $project
|
||||
my ($commitlist, $from, $to, $refs, $extra) = @_;
|
||||
|
||||
my $have_snapshot = gitweb_have_snapshot();
|
||||
|
||||
$from = 0 unless defined $from;
|
||||
$to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
|
||||
|
||||
@@ -3349,8 +3474,9 @@ sub git_shortlog_body {
|
||||
$cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
|
||||
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
|
||||
$cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree");
|
||||
if ($have_snapshot) {
|
||||
print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot");
|
||||
my $snapshot_links = format_snapshot_links($commit);
|
||||
if (defined $snapshot_links) {
|
||||
print " | " . $snapshot_links;
|
||||
}
|
||||
print "</td>\n" .
|
||||
"</tr>\n";
|
||||
@@ -4132,8 +4258,6 @@ sub git_blob {
|
||||
}
|
||||
|
||||
sub git_tree {
|
||||
my $have_snapshot = gitweb_have_snapshot();
|
||||
|
||||
if (!defined $hash_base) {
|
||||
$hash_base = "HEAD";
|
||||
}
|
||||
@@ -4167,11 +4291,10 @@ sub git_tree {
|
||||
hash_base=>"HEAD", file_name=>$file_name)},
|
||||
"HEAD"),
|
||||
}
|
||||
if ($have_snapshot) {
|
||||
my $snapshot_links = format_snapshot_links($hash);
|
||||
if (defined $snapshot_links) {
|
||||
# FIXME: Should be available when we have no hash base as well.
|
||||
push @views_nav,
|
||||
$cgi->a({-href => href(action=>"snapshot", hash=>$hash)},
|
||||
"snapshot");
|
||||
push @views_nav, $snapshot_links;
|
||||
}
|
||||
git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav));
|
||||
git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
|
||||
@@ -4235,33 +4358,44 @@ sub git_tree {
|
||||
}
|
||||
|
||||
sub git_snapshot {
|
||||
my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
|
||||
my $have_snapshot = (defined $ctype && defined $suffix);
|
||||
if (!$have_snapshot) {
|
||||
my @supported_fmts = gitweb_check_feature('snapshot');
|
||||
@supported_fmts = filter_snapshot_fmts(@supported_fmts);
|
||||
|
||||
my $format = $cgi->param('sf');
|
||||
if (!@supported_fmts) {
|
||||
die_error('403 Permission denied', "Permission denied");
|
||||
}
|
||||
# default to first supported snapshot format
|
||||
$format ||= $supported_fmts[0];
|
||||
if ($format !~ m/^[a-z0-9]+$/) {
|
||||
die_error(undef, "Invalid snapshot format parameter");
|
||||
} elsif (!exists($known_snapshot_formats{$format})) {
|
||||
die_error(undef, "Unknown snapshot format");
|
||||
} elsif (!grep($_ eq $format, @supported_fmts)) {
|
||||
die_error(undef, "Unsupported snapshot format");
|
||||
}
|
||||
|
||||
if (!defined $hash) {
|
||||
$hash = git_get_head_hash($project);
|
||||
}
|
||||
|
||||
my $git = git_cmd_str();
|
||||
my $git_command = git_cmd_str();
|
||||
my $name = $project;
|
||||
$name =~ s,([^/])/*\.git$,$1,;
|
||||
$name = basename($name);
|
||||
my $filename = to_utf8($name);
|
||||
$name =~ s/\047/\047\\\047\047/g;
|
||||
my $cmd;
|
||||
if ($suffix eq 'zip') {
|
||||
$filename .= "-$hash.$suffix";
|
||||
$cmd = "$git archive --format=zip --prefix=\'$name\'/ $hash";
|
||||
} else {
|
||||
$filename .= "-$hash.tar.$suffix";
|
||||
$cmd = "$git archive --format=tar --prefix=\'$name\'/ $hash | $command";
|
||||
$filename .= "-$hash$known_snapshot_formats{$format}{'suffix'}";
|
||||
$cmd = "$git_command archive " .
|
||||
"--format=$known_snapshot_formats{$format}{'format'} " .
|
||||
"--prefix=\'$name\'/ $hash";
|
||||
if (exists $known_snapshot_formats{$format}{'compressor'}) {
|
||||
$cmd .= ' | ' . join ' ', @{$known_snapshot_formats{$format}{'compressor'}};
|
||||
}
|
||||
|
||||
print $cgi->header(
|
||||
-type => "application/$ctype",
|
||||
-type => $known_snapshot_formats{$format}{'type'},
|
||||
-content_disposition => 'inline; filename="' . "$filename" . '"',
|
||||
-status => '200 OK');
|
||||
|
||||
@@ -4271,7 +4405,6 @@ sub git_snapshot {
|
||||
print <$fd>;
|
||||
binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
|
||||
close $fd;
|
||||
|
||||
}
|
||||
|
||||
sub git_log {
|
||||
@@ -4390,8 +4523,6 @@ sub git_commit {
|
||||
my $refs = git_get_references();
|
||||
my $ref = format_ref_marker($refs, $co{'id'});
|
||||
|
||||
my $have_snapshot = gitweb_have_snapshot();
|
||||
|
||||
git_header_html(undef, $expires);
|
||||
git_print_page_nav('commit', '',
|
||||
$hash, $co{'tree'}, $hash,
|
||||
@@ -4430,9 +4561,9 @@ sub git_commit {
|
||||
"<td class=\"link\">" .
|
||||
$cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash)},
|
||||
"tree");
|
||||
if ($have_snapshot) {
|
||||
print " | " .
|
||||
$cgi->a({-href => href(action=>"snapshot", hash=>$hash)}, "snapshot");
|
||||
my $snapshot_links = format_snapshot_links($hash);
|
||||
if (defined $snapshot_links) {
|
||||
print " | " . $snapshot_links;
|
||||
}
|
||||
print "</td>" .
|
||||
"</tr>\n";
|
||||
|
||||
13
lockfile.c
13
lockfile.c
@@ -29,6 +29,19 @@ static void remove_lock_file_on_signal(int signo)
|
||||
|
||||
static int lock_file(struct lock_file *lk, const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if ((!lstat(path, &st)) && S_ISLNK(st.st_mode)) {
|
||||
ssize_t sz;
|
||||
static char target[PATH_MAX];
|
||||
sz = readlink(path, target, sizeof(target));
|
||||
if (sz < 0)
|
||||
warning("Cannot readlink %s", path);
|
||||
else if (target[0] != '/')
|
||||
warning("Cannot lock target of relative symlink %s", path);
|
||||
else
|
||||
path = target;
|
||||
}
|
||||
sprintf(lk->filename, "%s.lock", path);
|
||||
lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
|
||||
if (0 <= lk->fd) {
|
||||
|
||||
2
mktag.c
2
mktag.c
@@ -120,7 +120,7 @@ int main(int argc, char **argv)
|
||||
|
||||
setup_git_directory();
|
||||
|
||||
if (read_pipe(0, &buffer, &size)) {
|
||||
if (read_fd(0, &buffer, &size)) {
|
||||
free(buffer);
|
||||
die("could not read from stdin");
|
||||
}
|
||||
|
||||
30
path.c
30
path.c
@@ -71,24 +71,22 @@ char *git_path(const char *fmt, ...)
|
||||
/* git_mkstemp() - create tmp file honoring TMPDIR variable */
|
||||
int git_mkstemp(char *path, size_t len, const char *template)
|
||||
{
|
||||
char *env, *pch = path;
|
||||
const char *tmp;
|
||||
size_t n;
|
||||
|
||||
if ((env = getenv("TMPDIR")) == NULL &&
|
||||
/* on Windows it is TMP and TEMP */
|
||||
(env = getenv("TMP")) == NULL &&
|
||||
(env = getenv("TEMP")) == NULL) {
|
||||
strcpy(pch, "/tmp/");
|
||||
len -= 5;
|
||||
pch += 5;
|
||||
} else {
|
||||
size_t n = snprintf(pch, len, "%s/", env);
|
||||
|
||||
len -= n;
|
||||
pch += n;
|
||||
tmp = getenv("TMPDIR");
|
||||
/* on Windows it is TMP and TEMP */
|
||||
if (!tmp)
|
||||
tmp = getenv("TMP");
|
||||
if (!tmp)
|
||||
tmp = getenv("TEMP");
|
||||
if (!tmp)
|
||||
tmp = "/tmp";
|
||||
n = snprintf(path, len, "%s/%s", tmp, template);
|
||||
if (len <= n) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
strlcpy(pch, template, len);
|
||||
|
||||
return mkstemp(path);
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ void init_reflog_walk(struct reflog_walk_info** info)
|
||||
*info = xcalloc(sizeof(struct reflog_walk_info), 1);
|
||||
}
|
||||
|
||||
void add_reflog_for_walk(struct reflog_walk_info *info,
|
||||
int add_reflog_for_walk(struct reflog_walk_info *info,
|
||||
struct commit *commit, const char *name)
|
||||
{
|
||||
unsigned long timestamp = 0;
|
||||
@@ -188,7 +188,7 @@ void add_reflog_for_walk(struct reflog_walk_info *info,
|
||||
}
|
||||
}
|
||||
if (!reflogs || reflogs->nr == 0)
|
||||
die("No reflogs found for '%s'", branch);
|
||||
return -1;
|
||||
path_list_insert(branch, &info->complete_reflogs)->util
|
||||
= reflogs;
|
||||
}
|
||||
@@ -200,13 +200,14 @@ void add_reflog_for_walk(struct reflog_walk_info *info,
|
||||
if (commit_reflog->recno < 0) {
|
||||
free(branch);
|
||||
free(commit_reflog);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
} else
|
||||
commit_reflog->recno = reflogs->nr - recno - 1;
|
||||
commit_reflog->reflogs = reflogs;
|
||||
|
||||
add_commit_info(commit, commit_reflog, &info->reflogs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define REFLOG_WALK_H
|
||||
|
||||
extern void init_reflog_walk(struct reflog_walk_info** info);
|
||||
extern void add_reflog_for_walk(struct reflog_walk_info *info,
|
||||
extern int add_reflog_for_walk(struct reflog_walk_info *info,
|
||||
struct commit *commit, const char *name);
|
||||
extern void fake_reflog_parent(struct reflog_walk_info *info,
|
||||
struct commit *commit);
|
||||
|
||||
32
revision.c
32
revision.c
@@ -118,10 +118,11 @@ static void add_pending_object_with_mode(struct rev_info *revs, struct object *o
|
||||
{
|
||||
if (revs->no_walk && (obj->flags & UNINTERESTING))
|
||||
die("object ranges do not make sense when not walking revisions");
|
||||
if (revs->reflog_info && obj->type == OBJ_COMMIT &&
|
||||
add_reflog_for_walk(revs->reflog_info,
|
||||
(struct commit *)obj, name))
|
||||
return;
|
||||
add_object_array_with_mode(obj, name, &revs->pending, mode);
|
||||
if (revs->reflog_info && obj->type == OBJ_COMMIT)
|
||||
add_reflog_for_walk(revs->reflog_info,
|
||||
(struct commit *)obj, name);
|
||||
}
|
||||
|
||||
void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
|
||||
@@ -1165,11 +1166,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
add_message_grep(revs, arg+7);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--extended-regexp")) {
|
||||
if (!strcmp(arg, "--extended-regexp") ||
|
||||
!strcmp(arg, "-E")) {
|
||||
regflags |= REG_EXTENDED;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--regexp-ignore-case")) {
|
||||
if (!strcmp(arg, "--regexp-ignore-case") ||
|
||||
!strcmp(arg, "-i")) {
|
||||
regflags |= REG_ICASE;
|
||||
continue;
|
||||
}
|
||||
@@ -1189,6 +1192,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
revs->reverse ^= 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-walk")) {
|
||||
revs->no_walk = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--do-walk")) {
|
||||
revs->no_walk = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
|
||||
if (opts > 0) {
|
||||
@@ -1323,16 +1334,17 @@ static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp
|
||||
|
||||
static void remove_duplicate_parents(struct commit *commit)
|
||||
{
|
||||
struct commit_list *p;
|
||||
struct commit_list **pp = &commit->parents;
|
||||
struct commit_list **pp, *p;
|
||||
|
||||
/* Examine existing parents while marking ones we have seen... */
|
||||
for (p = commit->parents; p; p = p->next) {
|
||||
pp = &commit->parents;
|
||||
while ((p = *pp) != NULL) {
|
||||
struct commit *parent = p->item;
|
||||
if (parent->object.flags & TMP_MARK)
|
||||
if (parent->object.flags & TMP_MARK) {
|
||||
*pp = p->next;
|
||||
continue;
|
||||
}
|
||||
parent->object.flags |= TMP_MARK;
|
||||
*pp = p;
|
||||
pp = &p->next;
|
||||
}
|
||||
/* ... and clear the temporary mark */
|
||||
|
||||
21
sha1_file.c
21
sha1_file.c
@@ -2314,27 +2314,36 @@ int has_sha1_file(const unsigned char *sha1)
|
||||
*
|
||||
* returns 0 if anything went fine and -1 otherwise
|
||||
*
|
||||
* The buffer is always NUL-terminated, not including it in returned size.
|
||||
*
|
||||
* NOTE: both buf and size may change, but even when -1 is returned
|
||||
* you still have to free() it yourself.
|
||||
*/
|
||||
int read_pipe(int fd, char** return_buf, unsigned long* return_size)
|
||||
int read_fd(int fd, char **return_buf, unsigned long *return_size)
|
||||
{
|
||||
char* buf = *return_buf;
|
||||
char *buf = *return_buf;
|
||||
unsigned long size = *return_size;
|
||||
ssize_t iret;
|
||||
unsigned long off = 0;
|
||||
|
||||
if (!buf || size <= 1) {
|
||||
size = 1024;
|
||||
buf = xrealloc(buf, size);
|
||||
}
|
||||
|
||||
do {
|
||||
iret = xread(fd, buf + off, size - off);
|
||||
iret = xread(fd, buf + off, (size - 1) - off);
|
||||
if (iret > 0) {
|
||||
off += iret;
|
||||
if (off == size) {
|
||||
size *= 2;
|
||||
if (off == size - 1) {
|
||||
size = alloc_nr(size);
|
||||
buf = xrealloc(buf, size);
|
||||
}
|
||||
}
|
||||
} while (iret > 0);
|
||||
|
||||
buf[off] = '\0';
|
||||
|
||||
*return_buf = buf;
|
||||
*return_size = off;
|
||||
|
||||
@@ -2349,7 +2358,7 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
|
||||
char *buf = xmalloc(size);
|
||||
int ret;
|
||||
|
||||
if (read_pipe(fd, &buf, &size)) {
|
||||
if (read_fd(fd, &buf, &size)) {
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -48,3 +48,37 @@ svnrepo="file://$svnrepo"
|
||||
poke() {
|
||||
test-chmtime +1 "$1"
|
||||
}
|
||||
|
||||
SVN_HTTPD_MODULE_PATH=${SVN_HTTPD_MODULE_PATH-'/usr/lib/apache2/modules'}
|
||||
SVN_HTTPD_PATH=${SVN_HTTPD_PATH-'/usr/sbin/apache2'}
|
||||
|
||||
start_httpd () {
|
||||
if test -z "$SVN_HTTPD_PORT"
|
||||
then
|
||||
echo >&2 'SVN_HTTPD_PORT is not defined!'
|
||||
return
|
||||
fi
|
||||
|
||||
mkdir "$GIT_DIR"/logs
|
||||
|
||||
cat > "$GIT_DIR/httpd.conf" <<EOF
|
||||
ServerName "git-svn test"
|
||||
ServerRoot "$GIT_DIR"
|
||||
DocumentRoot "$GIT_DIR"
|
||||
PidFile "$GIT_DIR/httpd.pid"
|
||||
Listen 127.0.0.1:$SVN_HTTPD_PORT
|
||||
LoadModule dav_module $SVN_HTTPD_MODULE_PATH/mod_dav.so
|
||||
LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so
|
||||
<Location /svn>
|
||||
DAV svn
|
||||
SVNPath $rawsvnrepo
|
||||
</Location>
|
||||
EOF
|
||||
"$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start
|
||||
svnrepo=http://127.0.0.1:$SVN_HTTPD_PORT/svn
|
||||
}
|
||||
|
||||
stop_httpd () {
|
||||
test -z "$SVN_HTTPD_PORT" && return
|
||||
"$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ cat > fake-editor.sh << EOF
|
||||
#!/bin/sh
|
||||
test "\$1" = .git/COMMIT_EDITMSG && {
|
||||
test -z "\$FAKE_COMMIT_MESSAGE" || echo "\$FAKE_COMMIT_MESSAGE" > "\$1"
|
||||
test -z "\$FAKE_COMMIT_AMEND" || echo "\$FAKE_COMMIT_AMEND" >> "\$1"
|
||||
exit
|
||||
}
|
||||
test -z "\$FAKE_LINES" && exit
|
||||
@@ -212,4 +213,42 @@ test_expect_success 'verbose flag is heeded, even after --continue' '
|
||||
grep "^ file1 | 2 +-$" output
|
||||
'
|
||||
|
||||
test_expect_success 'multi-squash only fires up editor once' '
|
||||
base=$(git rev-parse HEAD~4) &&
|
||||
FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \
|
||||
git rebase -i $base &&
|
||||
test $base = $(git rev-parse HEAD^) &&
|
||||
test 1 = $(git show | grep ONCE | wc -l)
|
||||
'
|
||||
|
||||
test_expect_success 'squash works as expected' '
|
||||
for n in one two three four
|
||||
do
|
||||
echo $n >> file$n &&
|
||||
git add file$n &&
|
||||
git commit -m $n
|
||||
done &&
|
||||
one=$(git rev-parse HEAD~3) &&
|
||||
FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
|
||||
test $one = $(git rev-parse HEAD~2)
|
||||
'
|
||||
|
||||
test_expect_success 'interrupted squash works as expected' '
|
||||
for n in one two three four
|
||||
do
|
||||
echo $n >> conflict &&
|
||||
git add conflict &&
|
||||
git commit -m $n
|
||||
done &&
|
||||
one=$(git rev-parse HEAD~3) &&
|
||||
! FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
|
||||
(echo one; echo two; echo four) > conflict &&
|
||||
git add conflict &&
|
||||
! git rebase --continue &&
|
||||
echo resolved > conflict &&
|
||||
git add conflict &&
|
||||
git rebase --continue &&
|
||||
test $one = $(git rev-parse HEAD~2)
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -66,4 +66,11 @@ test_expect_success 'apply stashed changes (including index)' '
|
||||
test 1 = $(git show HEAD:file)
|
||||
'
|
||||
|
||||
test_expect_success 'unstashing in a subdirectory' '
|
||||
git reset --hard HEAD &&
|
||||
mkdir subdir &&
|
||||
cd subdir &&
|
||||
git stash apply
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -30,24 +30,24 @@ test_expect_success 'setup' '
|
||||
H=$(git rev-parse H)
|
||||
|
||||
test_expect_success 'rewrite identically' '
|
||||
git-filter-branch H2
|
||||
git-filter-branch branch
|
||||
'
|
||||
|
||||
test_expect_success 'result is really identical' '
|
||||
test $H = $(git rev-parse H2)
|
||||
test $H = $(git rev-parse HEAD)
|
||||
'
|
||||
|
||||
test_expect_success 'rewrite, renaming a specific file' '
|
||||
git-filter-branch --tree-filter "mv d doh || :" H3
|
||||
git-filter-branch -f --tree-filter "mv d doh || :" HEAD
|
||||
'
|
||||
|
||||
test_expect_success 'test that the file was renamed' '
|
||||
test d = $(git show H3:doh)
|
||||
test d = $(git show HEAD:doh)
|
||||
'
|
||||
|
||||
git tag oldD H3~4
|
||||
git tag oldD HEAD~4
|
||||
test_expect_success 'rewrite one branch, keeping a side branch' '
|
||||
git-filter-branch --tree-filter "mv b boh || :" modD D..oldD
|
||||
git branch modD oldD &&
|
||||
git-filter-branch -f --tree-filter "mv b boh || :" D..modD
|
||||
'
|
||||
|
||||
test_expect_success 'common ancestor is still common (unchanged)' '
|
||||
@@ -69,7 +69,8 @@ test_expect_success 'filter subdirectory only' '
|
||||
git rm a &&
|
||||
test_tick &&
|
||||
git commit -m "again not subdir" &&
|
||||
git-filter-branch --subdirectory-filter subdir sub
|
||||
git branch sub &&
|
||||
git-filter-branch -f --subdirectory-filter subdir refs/heads/sub
|
||||
'
|
||||
|
||||
test_expect_success 'subdirectory filter result looks okay' '
|
||||
@@ -89,7 +90,8 @@ test_expect_success 'setup and filter history that requires --full-history' '
|
||||
test_tick &&
|
||||
git commit -m "again subdir on master" &&
|
||||
git merge branch &&
|
||||
git-filter-branch --subdirectory-filter subdir sub-master
|
||||
git branch sub-master &&
|
||||
git-filter-branch -f --subdirectory-filter subdir sub-master
|
||||
'
|
||||
|
||||
test_expect_success 'subdirectory filter result looks okay' '
|
||||
@@ -100,7 +102,8 @@ test_expect_success 'subdirectory filter result looks okay' '
|
||||
'
|
||||
|
||||
test_expect_success 'use index-filter to move into a subdirectory' '
|
||||
git-filter-branch --index-filter \
|
||||
git branch directorymoved &&
|
||||
git-filter-branch -f --index-filter \
|
||||
"git ls-files -s | sed \"s-\\t-&newsubdir/-\" |
|
||||
GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \
|
||||
git update-index --index-info &&
|
||||
@@ -108,9 +111,10 @@ test_expect_success 'use index-filter to move into a subdirectory' '
|
||||
test -z "$(git diff HEAD directorymoved:newsubdir)"'
|
||||
|
||||
test_expect_success 'stops when msg filter fails' '
|
||||
! git-filter-branch --msg-filter false nonono &&
|
||||
rm -rf .git-rewrite &&
|
||||
! git rev-parse nonono
|
||||
old=$(git rev-parse HEAD) &&
|
||||
! git-filter-branch -f --msg-filter false &&
|
||||
test $old = $(git rev-parse HEAD) &&
|
||||
rm -rf .git-rewrite
|
||||
'
|
||||
|
||||
test_expect_success 'author information is preserved' '
|
||||
@@ -118,7 +122,8 @@ test_expect_success 'author information is preserved' '
|
||||
git add i &&
|
||||
test_tick &&
|
||||
GIT_AUTHOR_NAME="B V Uips" git commit -m bvuips &&
|
||||
git-filter-branch --msg-filter "cat; \
|
||||
git branch preserved-author &&
|
||||
git-filter-branch -f --msg-filter "cat; \
|
||||
test \$GIT_COMMIT != $(git rev-parse master) || \
|
||||
echo Hallo" \
|
||||
preserved-author &&
|
||||
@@ -129,7 +134,8 @@ test_expect_success "remove a certain author's commits" '
|
||||
echo i > i &&
|
||||
test_tick &&
|
||||
git commit -m i i &&
|
||||
git-filter-branch --commit-filter "\
|
||||
git branch removed-author &&
|
||||
git-filter-branch -f --commit-filter "\
|
||||
if [ \"\$GIT_AUTHOR_NAME\" = \"B V Uips\" ];\
|
||||
then\
|
||||
shift;\
|
||||
@@ -148,4 +154,9 @@ test_expect_success "remove a certain author's commits" '
|
||||
test 0 = $(git rev-list --author="B V Uips" removed-author | wc -l)
|
||||
'
|
||||
|
||||
test_expect_success 'barf on invalid name' '
|
||||
! git filter-branch -f master xy-problem &&
|
||||
! git filter-branch -f HEAD^
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
91
t/t7005-editor.sh
Executable file
91
t/t7005-editor.sh
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='GIT_EDITOR, core.editor, and stuff'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
for i in GIT_EDITOR core_editor EDITOR VISUAL vi
|
||||
do
|
||||
cat >e-$i.sh <<-EOF
|
||||
echo "Edited by $i" >"\$1"
|
||||
EOF
|
||||
chmod +x e-$i.sh
|
||||
done
|
||||
unset vi
|
||||
mv e-vi.sh vi
|
||||
PATH=".:$PATH"
|
||||
unset EDITOR VISUAL GIT_EDITOR
|
||||
|
||||
test_expect_success setup '
|
||||
|
||||
msg="Hand edited" &&
|
||||
echo "$msg" >expect &&
|
||||
git add vi &&
|
||||
test_tick &&
|
||||
git commit -m "$msg" &&
|
||||
git show -s --pretty=oneline |
|
||||
sed -e "s/^[0-9a-f]* //" >actual &&
|
||||
diff actual expect
|
||||
|
||||
'
|
||||
|
||||
TERM=dumb
|
||||
export TERM
|
||||
test_expect_success 'dumb should error out when falling back on vi' '
|
||||
|
||||
if git commit --amend
|
||||
then
|
||||
echo "Oops?"
|
||||
exit 1
|
||||
else
|
||||
: happy
|
||||
fi
|
||||
'
|
||||
|
||||
TERM=vt100
|
||||
export TERM
|
||||
for i in vi EDITOR VISUAL core_editor GIT_EDITOR
|
||||
do
|
||||
echo "Edited by $i" >expect
|
||||
unset EDITOR VISUAL GIT_EDITOR
|
||||
git config --unset-all core.editor
|
||||
case "$i" in
|
||||
core_editor)
|
||||
git config core.editor ./e-core_editor.sh
|
||||
;;
|
||||
[A-Z]*)
|
||||
eval "$i=./e-$i.sh"
|
||||
export $i
|
||||
;;
|
||||
esac
|
||||
test_expect_success "Using $i" '
|
||||
git commit --amend &&
|
||||
git show -s --pretty=oneline |
|
||||
sed -e "s/^[0-9a-f]* //" >actual &&
|
||||
diff actual expect
|
||||
'
|
||||
done
|
||||
|
||||
unset EDITOR VISUAL GIT_EDITOR
|
||||
git config --unset-all core.editor
|
||||
for i in vi EDITOR VISUAL core_editor GIT_EDITOR
|
||||
do
|
||||
echo "Edited by $i" >expect
|
||||
case "$i" in
|
||||
core_editor)
|
||||
git config core.editor ./e-core_editor.sh
|
||||
;;
|
||||
[A-Z]*)
|
||||
eval "$i=./e-$i.sh"
|
||||
export $i
|
||||
;;
|
||||
esac
|
||||
test_expect_success "Using $i (override)" '
|
||||
git commit --amend &&
|
||||
git show -s --pretty=oneline |
|
||||
sed -e "s/^[0-9a-f]* //" >actual &&
|
||||
diff actual expect
|
||||
'
|
||||
done
|
||||
|
||||
test_done
|
||||
@@ -21,6 +21,10 @@ subcommands of git-submodule.
|
||||
# -add an entry to .gitmodules for submodule 'example'
|
||||
#
|
||||
test_expect_success 'Prepare submodule testing' '
|
||||
: > t &&
|
||||
git-add t &&
|
||||
git-commit -m "initial commit" &&
|
||||
git branch initial HEAD &&
|
||||
mkdir lib &&
|
||||
cd lib &&
|
||||
git init &&
|
||||
@@ -166,4 +170,9 @@ test_expect_success 'status should be "up-to-date" after update' '
|
||||
git-submodule status | grep "^ $rev1"
|
||||
'
|
||||
|
||||
test_expect_success 'checkout superproject with subproject already present' '
|
||||
git-checkout initial &&
|
||||
git-checkout master
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
96
t/t7500-commit.sh
Executable file
96
t/t7500-commit.sh
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2007 Steven Grimm
|
||||
#
|
||||
|
||||
test_description='git-commit
|
||||
|
||||
Tests for selected commit options.'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
commit_msg_is () {
|
||||
test "`git log --pretty=format:%s%b -1`" = "$1"
|
||||
}
|
||||
|
||||
# A sanity check to see if commit is working at all.
|
||||
test_expect_success 'a basic commit in an empty tree should succeed' '
|
||||
echo content > foo &&
|
||||
git add foo &&
|
||||
git commit -m "initial commit"
|
||||
'
|
||||
|
||||
test_expect_success 'nonexistent template file should return error' '
|
||||
echo changes >> foo &&
|
||||
git add foo &&
|
||||
! git commit --template "$PWD"/notexist
|
||||
'
|
||||
|
||||
test_expect_success 'nonexistent template file in config should return error' '
|
||||
git config commit.template "$PWD"/notexist &&
|
||||
! git commit &&
|
||||
git config --unset commit.template
|
||||
'
|
||||
|
||||
# From now on we'll use a template file that exists.
|
||||
TEMPLATE="$PWD"/template
|
||||
|
||||
test_expect_success 'unedited template should not commit' '
|
||||
echo "template line" > "$TEMPLATE" &&
|
||||
! git commit --template "$TEMPLATE"
|
||||
'
|
||||
|
||||
test_expect_success 'unedited template with comments should not commit' '
|
||||
echo "# comment in template" >> "$TEMPLATE" &&
|
||||
! git commit --template "$TEMPLATE"
|
||||
'
|
||||
|
||||
test_expect_success 'a Signed-off-by line by itself should not commit' '
|
||||
! GIT_EDITOR=../t7500/add-signed-off git commit --template "$TEMPLATE"
|
||||
'
|
||||
|
||||
test_expect_success 'adding comments to a template should not commit' '
|
||||
! GIT_EDITOR=../t7500/add-comments git commit --template "$TEMPLATE"
|
||||
'
|
||||
|
||||
test_expect_success 'adding real content to a template should commit' '
|
||||
GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" &&
|
||||
commit_msg_is "template linecommit message"
|
||||
'
|
||||
|
||||
test_expect_success '-t option should be short for --template' '
|
||||
echo "short template" > "$TEMPLATE" &&
|
||||
echo "new content" >> foo &&
|
||||
git add foo &&
|
||||
GIT_EDITOR=../t7500/add-content git commit -t "$TEMPLATE" &&
|
||||
commit_msg_is "short templatecommit message"
|
||||
'
|
||||
|
||||
test_expect_success 'config-specified template should commit' '
|
||||
echo "new template" > "$TEMPLATE" &&
|
||||
git config commit.template "$TEMPLATE" &&
|
||||
echo "more content" >> foo &&
|
||||
git add foo &&
|
||||
GIT_EDITOR=../t7500/add-content git commit &&
|
||||
git config --unset commit.template &&
|
||||
commit_msg_is "new templatecommit message"
|
||||
'
|
||||
|
||||
test_expect_success 'explicit commit message should override template' '
|
||||
echo "still more content" >> foo &&
|
||||
git add foo &&
|
||||
GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" \
|
||||
-m "command line msg" &&
|
||||
commit_msg_is "command line msg<unknown>"
|
||||
'
|
||||
|
||||
test_expect_success 'commit message from file should override template' '
|
||||
echo "content galore" >> foo &&
|
||||
git add foo &&
|
||||
echo "standard input msg" |
|
||||
GIT_EDITOR=../t7500/add-content git commit \
|
||||
--template "$TEMPLATE" --file - &&
|
||||
commit_msg_is "standard input msg<unknown>"
|
||||
'
|
||||
|
||||
test_done
|
||||
4
t/t7500/add-comments
Executable file
4
t/t7500/add-comments
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
echo "# this is a new comment" >> "$1"
|
||||
echo "# and so is this" >> "$1"
|
||||
exit 0
|
||||
3
t/t7500/add-content
Executable file
3
t/t7500/add-content
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
echo "commit message" >> "$1"
|
||||
exit 0
|
||||
3
t/t7500/add-signed-off
Executable file
3
t/t7500/add-signed-off
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
echo "Signed-off-by: foo <bar@frotz>" >> "$1"
|
||||
exit 0
|
||||
54
t/t9115-git-svn-dcommit-funky-renames.sh
Executable file
54
t/t9115-git-svn-dcommit-funky-renames.sh
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2007 Eric Wong
|
||||
|
||||
|
||||
test_description='git-svn dcommit can commit renames of files with ugly names'
|
||||
|
||||
. ./lib-git-svn.sh
|
||||
|
||||
test_expect_success 'load repository with strange names' "
|
||||
svnadmin load -q $rawsvnrepo < ../t9115/funky-names.dump &&
|
||||
start_httpd
|
||||
"
|
||||
|
||||
test_expect_success 'init and fetch repository' "
|
||||
git svn init $svnrepo &&
|
||||
git svn fetch &&
|
||||
git reset --hard git-svn
|
||||
"
|
||||
|
||||
test_expect_success 'create file in existing ugly and empty dir' '
|
||||
mkdir "#{bad_directory_name}" &&
|
||||
echo hi > "#{bad_directory_name}/ foo" &&
|
||||
git update-index --add "#{bad_directory_name}/ foo" &&
|
||||
git commit -m "new file in ugly parent" &&
|
||||
git svn dcommit
|
||||
'
|
||||
|
||||
test_expect_success 'rename ugly file' '
|
||||
git mv "#{bad_directory_name}/ foo" "file name with feces" &&
|
||||
git commit -m "rename ugly file" &&
|
||||
git svn dcommit
|
||||
'
|
||||
|
||||
test_expect_success 'rename pretty file' '
|
||||
echo :x > pretty &&
|
||||
git update-index --add pretty &&
|
||||
git commit -m "pretty :x" &&
|
||||
git svn dcommit &&
|
||||
mkdir regular_dir_name &&
|
||||
git mv pretty regular_dir_name/pretty &&
|
||||
git commit -m "moved pretty file" &&
|
||||
git svn dcommit
|
||||
'
|
||||
|
||||
test_expect_success 'rename pretty file into ugly one' '
|
||||
git mv regular_dir_name/pretty "#{bad_directory_name}/ booboo" &&
|
||||
git commit -m booboo &&
|
||||
git svn dcommit
|
||||
'
|
||||
|
||||
stop_httpd
|
||||
|
||||
test_done
|
||||
103
t/t9115/funky-names.dump
Normal file
103
t/t9115/funky-names.dump
Normal file
@@ -0,0 +1,103 @@
|
||||
SVN-fs-dump-format-version: 2
|
||||
|
||||
UUID: 819c44fe-2bcc-4066-88e4-985e2bc0b418
|
||||
|
||||
Revision-number: 0
|
||||
Prop-content-length: 56
|
||||
Content-length: 56
|
||||
|
||||
K 8
|
||||
svn:date
|
||||
V 27
|
||||
2007-07-12T07:54:26.062914Z
|
||||
PROPS-END
|
||||
|
||||
Revision-number: 1
|
||||
Prop-content-length: 152
|
||||
Content-length: 152
|
||||
|
||||
K 7
|
||||
svn:log
|
||||
V 44
|
||||
what will those wacky people think of next?
|
||||
|
||||
K 10
|
||||
svn:author
|
||||
V 12
|
||||
normalperson
|
||||
K 8
|
||||
svn:date
|
||||
V 27
|
||||
2007-07-12T08:00:05.011573Z
|
||||
PROPS-END
|
||||
|
||||
Node-path: leading space
|
||||
Node-kind: dir
|
||||
Node-action: add
|
||||
Prop-content-length: 10
|
||||
Content-length: 10
|
||||
|
||||
PROPS-END
|
||||
|
||||
|
||||
Node-path: leading space file
|
||||
Node-kind: file
|
||||
Node-action: add
|
||||
Prop-content-length: 10
|
||||
Text-content-length: 5
|
||||
Text-content-md5: e4fa20c67542cdc21271e08d329397ab
|
||||
Content-length: 15
|
||||
|
||||
PROPS-END
|
||||
ugly
|
||||
|
||||
|
||||
Node-path: #{bad_directory_name}
|
||||
Node-kind: dir
|
||||
Node-action: add
|
||||
Prop-content-length: 10
|
||||
Content-length: 10
|
||||
|
||||
PROPS-END
|
||||
|
||||
|
||||
Node-path: #{cool_name}
|
||||
Node-kind: file
|
||||
Node-action: add
|
||||
Prop-content-length: 10
|
||||
Text-content-length: 18
|
||||
Text-content-md5: 87dac40ca337dfa3dcc8911388c3ddda
|
||||
Content-length: 28
|
||||
|
||||
PROPS-END
|
||||
strange name here
|
||||
|
||||
|
||||
Node-path: dir name with spaces
|
||||
Node-kind: dir
|
||||
Node-action: add
|
||||
Prop-content-length: 10
|
||||
Content-length: 10
|
||||
|
||||
PROPS-END
|
||||
|
||||
|
||||
Node-path: file name with spaces
|
||||
Node-kind: file
|
||||
Node-action: add
|
||||
Prop-content-length: 10
|
||||
Text-content-length: 7
|
||||
Text-content-md5: c1f10cfd640618484a2a475c11410fd3
|
||||
Content-length: 17
|
||||
|
||||
PROPS-END
|
||||
spaces
|
||||
|
||||
|
||||
Node-path: regular_dir_name
|
||||
Node-kind: dir
|
||||
Node-action: add
|
||||
Prop-content-length: 10
|
||||
Content-length: 10
|
||||
|
||||
PROPS-END
|
||||
@@ -28,6 +28,18 @@ git add empty &&
|
||||
git commit -q -a -m "Initial" 2>/dev/null ||
|
||||
exit 1
|
||||
|
||||
check_entries () {
|
||||
# $1 == directory, $2 == expected
|
||||
grep '^/' "$1/CVS/Entries" | /usr/bin/sort | cut -d/ -f2,3,5 >actual
|
||||
if test -z "$2"
|
||||
then
|
||||
>expected
|
||||
else
|
||||
printf '%s\n' "$2" | tr '|' '\012' >expected
|
||||
fi
|
||||
diff -u expected actual
|
||||
}
|
||||
|
||||
test_expect_success \
|
||||
'New file' \
|
||||
'mkdir A B C D E F &&
|
||||
@@ -43,10 +55,10 @@ test_expect_success \
|
||||
id=$(git rev-list --max-count=1 HEAD) &&
|
||||
(cd "$CVSWORK" &&
|
||||
git cvsexportcommit -c $id &&
|
||||
test "$(echo $(/usr/bin/sort A/CVS/Entries|cut -d/ -f2,3,5))" = "newfile1.txt/1.1/" &&
|
||||
test "$(echo $(/usr/bin/sort B/CVS/Entries|cut -d/ -f2,3,5))" = "newfile2.txt/1.1/" &&
|
||||
test "$(echo $(/usr/bin/sort C/CVS/Entries|cut -d/ -f2,3,5))" = "newfile3.png/1.1/-kb" &&
|
||||
test "$(echo $(/usr/bin/sort D/CVS/Entries|cut -d/ -f2,3,5))" = "newfile4.png/1.1/-kb" &&
|
||||
check_entries A "newfile1.txt/1.1/" &&
|
||||
check_entries B "newfile2.txt/1.1/" &&
|
||||
check_entries C "newfile3.png/1.1/-kb" &&
|
||||
check_entries D "newfile4.png/1.1/-kb" &&
|
||||
diff A/newfile1.txt ../A/newfile1.txt &&
|
||||
diff B/newfile2.txt ../B/newfile2.txt &&
|
||||
diff C/newfile3.png ../C/newfile3.png &&
|
||||
@@ -67,12 +79,12 @@ test_expect_success \
|
||||
id=$(git rev-list --max-count=1 HEAD) &&
|
||||
(cd "$CVSWORK" &&
|
||||
git cvsexportcommit -c $id &&
|
||||
test "$(echo $(/usr/bin/sort A/CVS/Entries|cut -d/ -f2,3,5))" = "newfile1.txt/1.2/" &&
|
||||
test "$(echo $(/usr/bin/sort B/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
|
||||
test "$(echo $(/usr/bin/sort C/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
|
||||
test "$(echo $(/usr/bin/sort D/CVS/Entries|cut -d/ -f2,3,5))" = "newfile4.png/1.2/-kb" &&
|
||||
test "$(echo $(/usr/bin/sort E/CVS/Entries|cut -d/ -f2,3,5))" = "newfile5.txt/1.1/" &&
|
||||
test "$(echo $(/usr/bin/sort F/CVS/Entries|cut -d/ -f2,3,5))" = "newfile6.png/1.1/-kb" &&
|
||||
check_entries A "newfile1.txt/1.2/" &&
|
||||
check_entries B "" &&
|
||||
check_entries C "" &&
|
||||
check_entries D "newfile4.png/1.2/-kb" &&
|
||||
check_entries E "newfile5.txt/1.1/" &&
|
||||
check_entries F "newfile6.png/1.1/-kb" &&
|
||||
diff A/newfile1.txt ../A/newfile1.txt &&
|
||||
diff D/newfile4.png ../D/newfile4.png &&
|
||||
diff E/newfile5.txt ../E/newfile5.txt &&
|
||||
@@ -115,12 +127,12 @@ test_expect_success \
|
||||
id=$(git rev-list --max-count=1 HEAD) &&
|
||||
(cd "$CVSWORK" &&
|
||||
git cvsexportcommit -c $id &&
|
||||
test "$(echo $(/usr/bin/sort A/CVS/Entries|cut -d/ -f2,3,5))" = "newfile1.txt/1.2/" &&
|
||||
test "$(echo $(/usr/bin/sort B/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
|
||||
test "$(echo $(/usr/bin/sort C/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
|
||||
test "$(echo $(/usr/bin/sort D/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
|
||||
test "$(echo $(/usr/bin/sort E/CVS/Entries|cut -d/ -f2,3,5))" = "newfile5.txt/1.1/" &&
|
||||
test "$(echo $(/usr/bin/sort F/CVS/Entries|cut -d/ -f2,3,5))" = "newfile6.png/1.1/-kb" &&
|
||||
check_entries A "newfile1.txt/1.2/" &&
|
||||
check_entries B "" &&
|
||||
check_entries C "" &&
|
||||
check_entries D "" &&
|
||||
check_entries E "newfile5.txt/1.1/" &&
|
||||
check_entries F "newfile6.png/1.1/-kb" &&
|
||||
diff A/newfile1.txt ../A/newfile1.txt &&
|
||||
diff E/newfile5.txt ../E/newfile5.txt &&
|
||||
diff F/newfile6.png ../F/newfile6.png
|
||||
@@ -133,12 +145,12 @@ test_expect_success \
|
||||
id=$(git rev-list --max-count=1 HEAD) &&
|
||||
(cd "$CVSWORK" &&
|
||||
git cvsexportcommit -c $id &&
|
||||
test "$(echo $(/usr/bin/sort A/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
|
||||
test "$(echo $(/usr/bin/sort B/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
|
||||
test "$(echo $(/usr/bin/sort C/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
|
||||
test "$(echo $(/usr/bin/sort D/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
|
||||
test "$(echo $(/usr/bin/sort E/CVS/Entries|cut -d/ -f2,3,5))" = "newfile5.txt/1.1/" &&
|
||||
test "$(echo $(/usr/bin/sort F/CVS/Entries|cut -d/ -f2,3,5))" = "newfile6.png/1.1/-kb" &&
|
||||
check_entries A "" &&
|
||||
check_entries B "" &&
|
||||
check_entries C "" &&
|
||||
check_entries D "" &&
|
||||
check_entries E "newfile5.txt/1.1/" &&
|
||||
check_entries F "newfile6.png/1.1/-kb" &&
|
||||
diff E/newfile5.txt ../E/newfile5.txt &&
|
||||
diff F/newfile6.png ../F/newfile6.png
|
||||
)'
|
||||
@@ -154,7 +166,7 @@ test_expect_success \
|
||||
id=$(git rev-list --max-count=1 HEAD) &&
|
||||
(cd "$CVSWORK" &&
|
||||
git-cvsexportcommit -c $id &&
|
||||
test "$(echo $(/usr/bin/sort "G g/CVS/Entries"|cut -d/ -f2,3,5))" = "with spaces.png/1.1/-kb with spaces.txt/1.1/"
|
||||
check_entries "G g" "with spaces.png/1.1/-kb|with spaces.txt/1.1/"
|
||||
)'
|
||||
|
||||
test_expect_success \
|
||||
@@ -166,7 +178,7 @@ test_expect_success \
|
||||
id=$(git rev-list --max-count=1 HEAD) &&
|
||||
(cd "$CVSWORK" &&
|
||||
git-cvsexportcommit -c $id
|
||||
test "$(echo $(/usr/bin/sort "G g/CVS/Entries"|cut -d/ -f2,3,5))" = "with spaces.png/1.2/-kb with spaces.txt/1.2/"
|
||||
check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
|
||||
)'
|
||||
|
||||
# Some filesystems mangle pathnames with UTF-8 characters --
|
||||
@@ -191,7 +203,9 @@ test_expect_success \
|
||||
id=$(git rev-list --max-count=1 HEAD) &&
|
||||
(cd "$CVSWORK" &&
|
||||
git-cvsexportcommit -v -c $id &&
|
||||
test "$(echo $(/usr/bin/sortsort Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/CVS/Entries|cut -d/ -f2,3,5))" = "gårdetsågårdet.png/1.1/-kb gårdetsågårdet.txt/1.1/"
|
||||
check_entries \
|
||||
"Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" \
|
||||
"gårdetsågårdet.png/1.1/-kb|gårdetsågårdet.txt/1.1/"
|
||||
)'
|
||||
|
||||
fi
|
||||
|
||||
@@ -577,7 +577,7 @@ EXPECT_END
|
||||
test_expect_success \
|
||||
'L: verify internal tree sorting' \
|
||||
'git-fast-import <input &&
|
||||
git diff --raw L^ L >output &&
|
||||
git diff-tree --abbrev --raw L^ L >output &&
|
||||
git diff expect output'
|
||||
|
||||
###
|
||||
|
||||
@@ -521,4 +521,32 @@ test_expect_success \
|
||||
'gitweb_run "p=.git;a=log"'
|
||||
test_debug 'cat gitweb.log'
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# extra options
|
||||
|
||||
test_expect_success \
|
||||
'opt: log --no-merges' \
|
||||
'gitweb_run "p=.git;a=log;opt=--no-merges"'
|
||||
test_debug 'cat gitweb.log'
|
||||
|
||||
test_expect_success \
|
||||
'opt: atom --no-merges' \
|
||||
'gitweb_run "p=.git;a=log;opt=--no-merges"'
|
||||
test_debug 'cat gitweb.log'
|
||||
|
||||
test_expect_success \
|
||||
'opt: "file" history --no-merges' \
|
||||
'gitweb_run "p=.git;a=history;f=file;opt=--no-merges"'
|
||||
test_debug 'cat gitweb.log'
|
||||
|
||||
test_expect_success \
|
||||
'opt: log --no-such-option (invalid option)' \
|
||||
'gitweb_run "p=.git;a=log;opt=--no-such-option"'
|
||||
test_debug 'cat gitweb.log'
|
||||
|
||||
test_expect_success \
|
||||
'opt: tree --no-merges (invalid option for action)' \
|
||||
'gitweb_run "p=.git;a=tree;opt=--no-merges"'
|
||||
test_debug 'cat gitweb.log'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -5,14 +5,12 @@
|
||||
#include "cache-tree.h"
|
||||
#include "unpack-trees.h"
|
||||
#include "progress.h"
|
||||
#include "refs.h"
|
||||
|
||||
#define DBRT_DEBUG 1
|
||||
|
||||
struct tree_entry_list {
|
||||
struct tree_entry_list *next;
|
||||
unsigned directory : 1;
|
||||
unsigned executable : 1;
|
||||
unsigned symlink : 1;
|
||||
unsigned int mode;
|
||||
const char *name;
|
||||
const unsigned char *sha1;
|
||||
@@ -37,9 +35,6 @@ static struct tree_entry_list *create_tree_entry_list(struct tree *tree)
|
||||
entry->name = one.path;
|
||||
entry->sha1 = one.sha1;
|
||||
entry->mode = one.mode;
|
||||
entry->directory = S_ISDIR(one.mode) != 0;
|
||||
entry->executable = (one.mode & S_IXUSR) != 0;
|
||||
entry->symlink = S_ISLNK(one.mode) != 0;
|
||||
entry->next = NULL;
|
||||
|
||||
*list_p = entry;
|
||||
@@ -140,9 +135,9 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
|
||||
#endif
|
||||
if (!first || entcmp(first, firstdir,
|
||||
posns[i]->name,
|
||||
posns[i]->directory) > 0) {
|
||||
S_ISDIR(posns[i]->mode)) > 0) {
|
||||
first = posns[i]->name;
|
||||
firstdir = posns[i]->directory;
|
||||
firstdir = S_ISDIR(posns[i]->mode);
|
||||
}
|
||||
}
|
||||
/* No name means we're done */
|
||||
@@ -176,7 +171,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (posns[i]->directory) {
|
||||
if (S_ISDIR(posns[i]->mode)) {
|
||||
struct tree *tree = lookup_tree(posns[i]->sha1);
|
||||
any_dirs = 1;
|
||||
parse_tree(tree);
|
||||
@@ -425,11 +420,24 @@ static void invalidate_ce_path(struct cache_entry *ce)
|
||||
cache_tree_invalidate_path(active_cache_tree, ce->name);
|
||||
}
|
||||
|
||||
static int verify_clean_subdirectory(const char *path, const char *action,
|
||||
/*
|
||||
* Check that checking out ce->sha1 in subdir ce->name is not
|
||||
* going to overwrite any working files.
|
||||
*
|
||||
* Currently, git does not checkout subprojects during a superproject
|
||||
* checkout, so it is not going to overwrite anything.
|
||||
*/
|
||||
static int verify_clean_submodule(struct cache_entry *ce, const char *action,
|
||||
struct unpack_trees_options *o)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
|
||||
struct unpack_trees_options *o)
|
||||
{
|
||||
/*
|
||||
* we are about to extract "path"; we would not want to lose
|
||||
* we are about to extract "ce->name"; we would not want to lose
|
||||
* anything in the existing directory there.
|
||||
*/
|
||||
int namelen;
|
||||
@@ -437,13 +445,24 @@ static int verify_clean_subdirectory(const char *path, const char *action,
|
||||
struct dir_struct d;
|
||||
char *pathbuf;
|
||||
int cnt = 0;
|
||||
unsigned char sha1[20];
|
||||
|
||||
if (S_ISGITLINK(ntohl(ce->ce_mode)) &&
|
||||
resolve_gitlink_ref(ce->name, "HEAD", sha1) == 0) {
|
||||
/* If we are not going to update the submodule, then
|
||||
* we don't care.
|
||||
*/
|
||||
if (!hashcmp(sha1, ce->sha1))
|
||||
return 0;
|
||||
return verify_clean_submodule(ce, action, o);
|
||||
}
|
||||
|
||||
/*
|
||||
* First let's make sure we do not have a local modification
|
||||
* in that directory.
|
||||
*/
|
||||
namelen = strlen(path);
|
||||
pos = cache_name_pos(path, namelen);
|
||||
namelen = strlen(ce->name);
|
||||
pos = cache_name_pos(ce->name, namelen);
|
||||
if (0 <= pos)
|
||||
return cnt; /* we have it as nondirectory */
|
||||
pos = -pos - 1;
|
||||
@@ -451,7 +470,7 @@ static int verify_clean_subdirectory(const char *path, const char *action,
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
int len = ce_namelen(ce);
|
||||
if (len < namelen ||
|
||||
strncmp(path, ce->name, namelen) ||
|
||||
strncmp(ce->name, ce->name, namelen) ||
|
||||
ce->name[namelen] != '/')
|
||||
break;
|
||||
/*
|
||||
@@ -469,16 +488,16 @@ static int verify_clean_subdirectory(const char *path, const char *action,
|
||||
* present file that is not ignored.
|
||||
*/
|
||||
pathbuf = xmalloc(namelen + 2);
|
||||
memcpy(pathbuf, path, namelen);
|
||||
memcpy(pathbuf, ce->name, namelen);
|
||||
strcpy(pathbuf+namelen, "/");
|
||||
|
||||
memset(&d, 0, sizeof(d));
|
||||
if (o->dir)
|
||||
d.exclude_per_dir = o->dir->exclude_per_dir;
|
||||
i = read_directory(&d, path, pathbuf, namelen+1, NULL);
|
||||
i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL);
|
||||
if (i)
|
||||
die("Updating '%s' would lose untracked files in it",
|
||||
path);
|
||||
ce->name);
|
||||
free(pathbuf);
|
||||
return cnt;
|
||||
}
|
||||
@@ -487,7 +506,7 @@ static int verify_clean_subdirectory(const char *path, const char *action,
|
||||
* We do not want to remove or overwrite a working tree file that
|
||||
* is not tracked, unless it is ignored.
|
||||
*/
|
||||
static void verify_absent(const char *path, const char *action,
|
||||
static void verify_absent(struct cache_entry *ce, const char *action,
|
||||
struct unpack_trees_options *o)
|
||||
{
|
||||
struct stat st;
|
||||
@@ -495,15 +514,15 @@ static void verify_absent(const char *path, const char *action,
|
||||
if (o->index_only || o->reset || !o->update)
|
||||
return;
|
||||
|
||||
if (has_symlink_leading_path(path, NULL))
|
||||
if (has_symlink_leading_path(ce->name, NULL))
|
||||
return;
|
||||
|
||||
if (!lstat(path, &st)) {
|
||||
if (!lstat(ce->name, &st)) {
|
||||
int cnt;
|
||||
|
||||
if (o->dir && excluded(o->dir, path))
|
||||
if (o->dir && excluded(o->dir, ce->name))
|
||||
/*
|
||||
* path is explicitly excluded, so it is Ok to
|
||||
* ce->name is explicitly excluded, so it is Ok to
|
||||
* overwrite it.
|
||||
*/
|
||||
return;
|
||||
@@ -515,7 +534,7 @@ static void verify_absent(const char *path, const char *action,
|
||||
* files that are in "foo/" we would lose
|
||||
* it.
|
||||
*/
|
||||
cnt = verify_clean_subdirectory(path, action, o);
|
||||
cnt = verify_clean_subdirectory(ce, action, o);
|
||||
|
||||
/*
|
||||
* If this removed entries from the index,
|
||||
@@ -543,7 +562,7 @@ static void verify_absent(const char *path, const char *action,
|
||||
* delete this path, which is in a subdirectory that
|
||||
* is being replaced with a blob.
|
||||
*/
|
||||
cnt = cache_name_pos(path, strlen(path));
|
||||
cnt = cache_name_pos(ce->name, strlen(ce->name));
|
||||
if (0 <= cnt) {
|
||||
struct cache_entry *ce = active_cache[cnt];
|
||||
if (!ce_stage(ce) && !ce->ce_mode)
|
||||
@@ -551,7 +570,7 @@ static void verify_absent(const char *path, const char *action,
|
||||
}
|
||||
|
||||
die("Untracked working tree file '%s' "
|
||||
"would be %s by merge.", path, action);
|
||||
"would be %s by merge.", ce->name, action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,7 +594,7 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
|
||||
}
|
||||
}
|
||||
else {
|
||||
verify_absent(merge->name, "overwritten", o);
|
||||
verify_absent(merge, "overwritten", o);
|
||||
invalidate_ce_path(merge);
|
||||
}
|
||||
|
||||
@@ -590,7 +609,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
|
||||
if (old)
|
||||
verify_uptodate(old, o);
|
||||
else
|
||||
verify_absent(ce->name, "removed", o);
|
||||
verify_absent(ce, "removed", o);
|
||||
ce->ce_mode = 0;
|
||||
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
|
||||
invalidate_ce_path(ce);
|
||||
@@ -707,18 +726,18 @@ int threeway_merge(struct cache_entry **stages,
|
||||
if (o->aggressive) {
|
||||
int head_deleted = !head && !df_conflict_head;
|
||||
int remote_deleted = !remote && !df_conflict_remote;
|
||||
const char *path = NULL;
|
||||
struct cache_entry *ce = NULL;
|
||||
|
||||
if (index)
|
||||
path = index->name;
|
||||
ce = index;
|
||||
else if (head)
|
||||
path = head->name;
|
||||
ce = head;
|
||||
else if (remote)
|
||||
path = remote->name;
|
||||
ce = remote;
|
||||
else {
|
||||
for (i = 1; i < o->head_idx; i++) {
|
||||
if (stages[i] && stages[i] != o->df_conflict_entry) {
|
||||
path = stages[i]->name;
|
||||
ce = stages[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -733,8 +752,8 @@ int threeway_merge(struct cache_entry **stages,
|
||||
(remote_deleted && head && head_match)) {
|
||||
if (index)
|
||||
return deleted_entry(index, index, o);
|
||||
else if (path && !head_deleted)
|
||||
verify_absent(path, "removed", o);
|
||||
else if (ce && !head_deleted)
|
||||
verify_absent(ce, "removed", o);
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user