mirror of
https://github.com/git/git.git
synced 2026-03-13 10:23:30 +01:00
Merge commit 'junio/master'
Conflicts: Makefile builtin-config.c cache.h config.c git-gui/git-gui.sh lockfile.c path.c setup.c t/t9200-git-cvsexportcommit.sh Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -148,6 +148,7 @@ git-write-tree
|
||||
git-core-*/?*
|
||||
gitk-wish
|
||||
gitweb/gitweb.cgi
|
||||
test-absolute-path
|
||||
test-chmtime
|
||||
test-date
|
||||
test-delta
|
||||
|
||||
@@ -120,6 +120,7 @@ clean:
|
||||
mv $@+ $@
|
||||
|
||||
%.1 %.5 %.7 : %.xml
|
||||
$(RM) $@
|
||||
xmlto -m callouts.xsl man $<
|
||||
|
||||
%.xml : %.txt
|
||||
@@ -131,7 +132,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,23 +32,34 @@ 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
|
||||
not located at "$GIT_DIR/..".
|
||||
|
||||
- Giving "--file=<file>" option to "git config" is the same as
|
||||
running the command with GIT_CONFIG=<file> environment.
|
||||
|
||||
- "git log" learned a new option "--follow", to follow
|
||||
renaming history of a single file.
|
||||
|
||||
- "git-filter-branch" lets you rewrite the revision history of
|
||||
the current branch, creating a new branch. You can specify a
|
||||
number of filters to modify the commits, files and trees.
|
||||
specified branches. You can specify a number of filters to
|
||||
modify the commits, files and trees.
|
||||
|
||||
- "git-cvsserver" learned new options (--base-path, --export-all,
|
||||
--strict-paths) inspired by git-daemon.
|
||||
|
||||
- "git daemon --base-path-relaxed" can help migrating a repository URL
|
||||
that did not use to use --base-path to use --base-path.
|
||||
|
||||
- "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 +116,33 @@ 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.
|
||||
|
||||
- "git diff" (but not the plumbing level "git diff-tree") now
|
||||
recursively descends into trees by default.
|
||||
|
||||
- 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
|
||||
@@ -139,6 +174,9 @@ Updates since v1.5.2
|
||||
- The diffstat given after a merge (or a pull) honors the
|
||||
color.diff configuration.
|
||||
|
||||
- "git commit --amend" is now compatible with various message source
|
||||
options such as -m/-C/-c/-F.
|
||||
|
||||
- "git-apply --whitespace=strip" removes blank lines added at
|
||||
the end of the file.
|
||||
|
||||
@@ -189,6 +227,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 +274,6 @@ this release, unless otherwise noted.
|
||||
|
||||
--
|
||||
exec >/var/tmp/1
|
||||
O=v1.5.3-rc2
|
||||
O=v1.5.3-rc4
|
||||
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[]
|
||||
|
||||
@@ -101,7 +101,7 @@ Example
|
||||
|
||||
# Proxy settings
|
||||
[core]
|
||||
gitProxy="ssh" for "ssh://kernel.org/"
|
||||
gitProxy="ssh" for "kernel.org"
|
||||
gitProxy=default-proxy ; for the rest
|
||||
|
||||
Variables
|
||||
@@ -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>
|
||||
@@ -3,7 +3,7 @@ git-add(1)
|
||||
|
||||
NAME
|
||||
----
|
||||
git-add - Add file contents to the changeset to be committed next
|
||||
git-add - Add file contents to the index
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
@@ -11,24 +11,27 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
All the changed file contents to be committed together in a single set
|
||||
of changes must be "added" with the 'add' command before using the
|
||||
'commit' command. This is not only for adding new files. Even modified
|
||||
files must be added to the set of changes about to be committed.
|
||||
This command adds the current content of new or modified files to the
|
||||
index, thus staging that content for inclusion in the next commit.
|
||||
|
||||
This command can be performed multiple times before a commit. The added
|
||||
content corresponds to the state of specified file(s) at the time the
|
||||
'add' command is used. This means the 'commit' command will not consider
|
||||
subsequent changes to already added content if it is not added again before
|
||||
the commit.
|
||||
The "index" holds a snapshot of the content of the working tree, and it
|
||||
is this snapshot that is taken as the contents of the next commit. Thus
|
||||
after making any changes to the working directory, and before running
|
||||
the commit command, you must use the 'add' command to add any new or
|
||||
modified files to the index.
|
||||
|
||||
The 'git status' command can be used to obtain a summary of what is included
|
||||
for the next commit.
|
||||
This command can be performed multiple times before a commit. It only
|
||||
adds the content of the specified file(s) at the time the add command is
|
||||
run; if you want subsequent changes included in the next commit, then
|
||||
you must run 'git add' again to add the new content to the index.
|
||||
|
||||
This command can be used to add ignored files with `-f` (force)
|
||||
option, but they have to be
|
||||
explicitly and exactly specified from the command line. File globbing
|
||||
and recursive behaviour do not add ignored files.
|
||||
The 'git status' command can be used to obtain a summary of which
|
||||
files have changes that are staged for the next commit.
|
||||
|
||||
The 'add' command can be used to add ignored files with `-f` (force)
|
||||
option, but they have to be explicitly and exactly specified from the
|
||||
command line. File globbing and recursive behaviour do not add ignored
|
||||
files.
|
||||
|
||||
Please see gitlink:git-commit[1] for alternative ways to add content to a
|
||||
commit.
|
||||
|
||||
@@ -130,12 +130,12 @@ 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"
|
||||
<2> delete "test" branch even if the "master" branch does not have all
|
||||
<1> Delete remote-tracking branches "todo", "html", "man"
|
||||
<2> Delete "test" branch even if the "master" branch does not have all
|
||||
commits from test branch.
|
||||
|
||||
|
||||
|
||||
@@ -15,26 +15,27 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Use 'git commit' when you want to record your changes into the repository
|
||||
along with a log message describing what the commit is about. All changes
|
||||
to be committed must be explicitly identified using one of the following
|
||||
methods:
|
||||
Use 'git commit' to store the current contents of the index in a new
|
||||
commit along with a log message describing the changes you have made.
|
||||
|
||||
The content to be added can be specified in several ways:
|
||||
|
||||
1. by using gitlink:git-add[1] to incrementally "add" changes to the
|
||||
next commit before using the 'commit' command (Note: even modified
|
||||
index before using the 'commit' command (Note: even modified
|
||||
files must be "added");
|
||||
|
||||
2. by using gitlink:git-rm[1] to identify content removal for the next
|
||||
commit, again before using the 'commit' command;
|
||||
2. by using gitlink:git-rm[1] to remove files from the working tree
|
||||
and the index, again before using the 'commit' command;
|
||||
|
||||
3. by directly listing files containing changes to be committed as arguments
|
||||
to the 'commit' command, in which cases only those files alone will be
|
||||
considered for the commit;
|
||||
3. by listing files as arguments to the 'commit' command, in which
|
||||
case the commit will ignore changes staged in the index, and instead
|
||||
record the current content of the listed files;
|
||||
|
||||
4. by using the -a switch with the 'commit' command to automatically "add"
|
||||
changes from all known files i.e. files that have already been committed
|
||||
before, and to automatically "rm" files that have been
|
||||
removed from the working tree, and perform the actual commit.
|
||||
4. by using the -a switch with the 'commit' command to automatically
|
||||
"add" changes from all known files (i.e. all files that are already
|
||||
listed in the index) and to automatically "rm" files in the index
|
||||
that have been removed from the working tree, and then perform the
|
||||
actual commit;
|
||||
|
||||
5. by using the --interactive switch with the 'commit' command to decide one
|
||||
by one which files should be part of the commit, before finalizing the
|
||||
@@ -74,6 +75,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 +252,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
|
||||
-----
|
||||
|
||||
@@ -9,17 +9,17 @@ git-config - Get and set repository or global options
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-config' [--system | --global] [type] [-z|--null] name [value [value_regex]]
|
||||
'git-config' [--system | --global] [type] --add name value
|
||||
'git-config' [--system | --global] [type] --replace-all name [value [value_regex]]
|
||||
'git-config' [--system | --global] [type] [-z|--null] --get name [value_regex]
|
||||
'git-config' [--system | --global] [type] [-z|--null] --get-all name [value_regex]
|
||||
'git-config' [--system | --global] [type] [-z|--null] --get-regexp name_regex [value_regex]
|
||||
'git-config' [--system | --global] --unset name [value_regex]
|
||||
'git-config' [--system | --global] --unset-all name [value_regex]
|
||||
'git-config' [--system | --global] --rename-section old_name new_name
|
||||
'git-config' [--system | --global] --remove-section name
|
||||
'git-config' [--system | --global] [-z|--null] -l | --list
|
||||
'git-config' [<file-option>] [type] [-z|--null] name [value [value_regex]]
|
||||
'git-config' [<file-option>] [type] --add name value
|
||||
'git-config' [<file-option>] [type] --replace-all name [value [value_regex]]
|
||||
'git-config' [<file-option>] [type] [-z|--null] --get name [value_regex]
|
||||
'git-config' [<file-option>] [type] [-z|--null] --get-all name [value_regex]
|
||||
'git-config' [<file-option>] [type] [-z|--null] --get-regexp name_regex [value_regex]
|
||||
'git-config' [<file-option>] --unset name [value_regex]
|
||||
'git-config' [<file-option>] --unset-all name [value_regex]
|
||||
'git-config' [<file-option>] --rename-section old_name new_name
|
||||
'git-config' [<file-option>] --remove-section name
|
||||
'git-config' [<file-option>] [-z|--null] -l | --list
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -40,10 +40,16 @@ convert the value to the canonical form (simple decimal number for int,
|
||||
a "true" or "false" string for bool). If no type specifier is passed,
|
||||
no checks or transformations are performed on the value.
|
||||
|
||||
The file-option can be one of '--system', '--global' or '--file'
|
||||
which specify where the values will be read from or written to.
|
||||
The default is to assume the config file of the current repository,
|
||||
.git/config unless defined otherwise with GIT_DIR and GIT_CONFIG
|
||||
(see <<FILES>>).
|
||||
|
||||
This command will fail if:
|
||||
|
||||
. The .git/config file is invalid,
|
||||
. Can not write to .git/config,
|
||||
. The config file is invalid,
|
||||
. Can not write to the config file,
|
||||
. no section was provided,
|
||||
. the section or key is invalid,
|
||||
. you try to unset an option which does not exist,
|
||||
@@ -93,6 +99,9 @@ rather than from all available files.
|
||||
+
|
||||
See also <<FILES>>.
|
||||
|
||||
-f config-file, --file config-file::
|
||||
Use the given config file instead of the one specified by GIT_CONFIG.
|
||||
|
||||
--remove-section::
|
||||
Remove the given section from the configuration file.
|
||||
|
||||
@@ -130,8 +139,8 @@ See also <<FILES>>.
|
||||
FILES
|
||||
-----
|
||||
|
||||
There are three files where git-config will search for configuration
|
||||
options:
|
||||
If not set explicitely with '--file', there are three files where
|
||||
git-config will search for configuration options:
|
||||
|
||||
.git/config::
|
||||
Repository specific configuration file. (The filename is
|
||||
@@ -205,9 +214,7 @@ Given a .git/config like this:
|
||||
|
||||
; Proxy settings
|
||||
[core]
|
||||
gitproxy="ssh" for "ssh://kernel.org/"
|
||||
gitproxy="proxy-command" for kernel.org
|
||||
gitproxy="myprotocol-command" for "my://"
|
||||
gitproxy=default-proxy ; for all the rest
|
||||
|
||||
you can set the filemode to true with
|
||||
@@ -282,7 +289,7 @@ To actually match only values with an exclamation mark, you have to
|
||||
To add a new proxy, without altering any of the existing ones, use
|
||||
|
||||
------------
|
||||
% git config core.gitproxy '"proxy" for example.com'
|
||||
% git config core.gitproxy '"proxy-command" for example.com'
|
||||
------------
|
||||
|
||||
|
||||
|
||||
@@ -54,6 +54,12 @@ OPTIONS
|
||||
'git://example.com/hello.git', `git-daemon` will interpret the path
|
||||
as '/srv/git/hello.git'.
|
||||
|
||||
--base-path-relaxed::
|
||||
If --base-path is enabled and repo lookup fails, with this option
|
||||
`git-daemon` will attempt to lookup without prefixing the base path.
|
||||
This is useful for switching to --base-path usage, while still
|
||||
allowing the old paths.
|
||||
|
||||
--interpolated-path=pathtemplate::
|
||||
To support virtual hosting, an interpolated path template can be
|
||||
used to dynamically construct alternate paths. The template
|
||||
|
||||
@@ -76,10 +76,10 @@ $ git diff --cached <2>
|
||||
$ git diff HEAD <3>
|
||||
------------
|
||||
+
|
||||
<1> changes in the working tree not yet staged for the next commit.
|
||||
<2> changes between the index and your last commit; what you
|
||||
<1> Changes in the working tree not yet staged for the next commit.
|
||||
<2> Changes between the index and your last commit; what you
|
||||
would be committing if you run "git commit" without "-a" option.
|
||||
<3> changes in the working tree since your last commit; what you
|
||||
<3> Changes in the working tree since your last commit; what you
|
||||
would be committing if you run "git commit -a"
|
||||
|
||||
Comparing with arbitrary commits::
|
||||
@@ -90,30 +90,27 @@ $ git diff HEAD -- ./test <2>
|
||||
$ git diff HEAD^ HEAD <3>
|
||||
------------
|
||||
+
|
||||
<1> instead of using the tip of the current branch, compare with the
|
||||
<1> Instead of using the tip of the current branch, compare with the
|
||||
tip of "test" branch.
|
||||
<2> instead of comparing with the tip of "test" branch, compare with
|
||||
<2> Instead of comparing with the tip of "test" branch, compare with
|
||||
the tip of the current branch, but limit the comparison to the
|
||||
file "test".
|
||||
<3> compare the version before the last commit and the last commit.
|
||||
<3> Compare the version before the last commit and the last commit.
|
||||
|
||||
|
||||
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
|
||||
<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.
|
||||
<3> limit diff output to named subtrees.
|
||||
<2> Show only names and the nature of change, but not actual
|
||||
diff output.
|
||||
<3> Limit diff output to named subtrees.
|
||||
|
||||
Munging the diff output::
|
||||
+
|
||||
@@ -122,9 +119,9 @@ $ git diff --find-copies-harder -B -C <1>
|
||||
$ git diff -R <2>
|
||||
------------
|
||||
+
|
||||
<1> spend extra cycles to find renames, copies and complete
|
||||
<1> Spend extra cycles to find renames, copies and complete
|
||||
rewrites (very expensive).
|
||||
<2> output diff in reverse.
|
||||
<2> Output diff in reverse.
|
||||
|
||||
|
||||
Author
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -107,11 +107,11 @@ pull after you are done and ready.
|
||||
|
||||
When things cleanly merge, these things happen:
|
||||
|
||||
1. the results are updated both in the index file and in your
|
||||
working tree,
|
||||
2. index file is written out as a tree,
|
||||
3. the tree gets committed, and
|
||||
4. the `HEAD` pointer gets advanced.
|
||||
1. The results are updated both in the index file and in your
|
||||
working tree;
|
||||
2. Index file is written out as a tree;
|
||||
3. The tree gets committed; and
|
||||
4. The `HEAD` pointer gets advanced.
|
||||
|
||||
Because of 2., we require that the original state of the index
|
||||
file to match exactly the current `HEAD` commit; otherwise we
|
||||
|
||||
@@ -79,7 +79,7 @@ the remote repository.
|
||||
|
||||
-f, \--force::
|
||||
Usually, the command refuses to update a remote ref that is
|
||||
not a descendant of the local ref used to overwrite it.
|
||||
not an ancestor of the local ref used to overwrite it.
|
||||
This flag disables the check. This can cause the
|
||||
remote repository to lose commits; use it with care.
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ $ git commit -a -c ORIG_HEAD <3>
|
||||
<1> This is most often done when you remembered what you
|
||||
just committed is incomplete, or you misspelled your commit
|
||||
message, or both. Leaves working tree as it was before "reset".
|
||||
<2> make corrections to working tree files.
|
||||
<2> Make corrections to working tree files.
|
||||
<3> "reset" copies the old head to .git/ORIG_HEAD; redo the
|
||||
commit by starting with its log message. If you do not need to
|
||||
edit the message further, you can give -C option instead.
|
||||
@@ -106,17 +106,17 @@ $ git reset <3>
|
||||
$ git pull git://info.example.com/ nitfol <4>
|
||||
------------
|
||||
+
|
||||
<1> you are happily working on something, and find the changes
|
||||
<1> You are happily working on something, and find the changes
|
||||
in these files are in good order. You do not want to see them
|
||||
when you run "git diff", because you plan to work on other files
|
||||
and changes with these files are distracting.
|
||||
<2> somebody asks you to pull, and the changes sounds worthy of merging.
|
||||
<3> however, you already dirtied the index (i.e. your index does
|
||||
<2> Somebody asks you to pull, and the changes sounds worthy of merging.
|
||||
<3> However, you already dirtied the index (i.e. your index does
|
||||
not match the HEAD commit). But you know the pull you are going
|
||||
to make does not affect frotz.c nor filfre.c, so you revert the
|
||||
index changes for these two files. Your changes in working tree
|
||||
remain there.
|
||||
<4> then you can pull and merge, leaving frotz.c and filfre.c
|
||||
<4> Then you can pull and merge, leaving frotz.c and filfre.c
|
||||
changes still in the working tree.
|
||||
|
||||
Undo a merge or pull::
|
||||
@@ -133,15 +133,15 @@ Fast forward
|
||||
$ git reset --hard ORIG_HEAD <4>
|
||||
------------
|
||||
+
|
||||
<1> try to update from the upstream resulted in a lot of
|
||||
<1> Try to update from the upstream resulted in a lot of
|
||||
conflicts; you were not ready to spend a lot of time merging
|
||||
right now, so you decide to do that later.
|
||||
<2> "pull" has not made merge commit, so "git reset --hard"
|
||||
which is a synonym for "git reset --hard HEAD" clears the mess
|
||||
from the index file and the working tree.
|
||||
<3> merge a topic branch into the current branch, which resulted
|
||||
<3> Merge a topic branch into the current branch, which resulted
|
||||
in a fast forward.
|
||||
<4> but you decided that the topic branch is not ready for public
|
||||
<4> But you decided that the topic branch is not ready for public
|
||||
consumption yet. "pull" or "merge" always leaves the original
|
||||
tip of the current branch in ORIG_HEAD, so resetting hard to it
|
||||
brings your index file and the working tree back to that state,
|
||||
|
||||
@@ -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,12 +23,14 @@ 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
|
||||
the usual reflog syntax (e.g. `stash@\{1}` is the most recently
|
||||
created stash, `stash@\{2}` is the one before it, `stash@\{2.hours.ago}`
|
||||
the usual reflog syntax (e.g. `stash@\{0}` is the most recently
|
||||
created stash, `stash@\{1}` is the one before it, `stash@\{2.hours.ago}`
|
||||
is also possible).
|
||||
|
||||
OPTIONS
|
||||
@@ -42,14 +45,14 @@ save::
|
||||
list::
|
||||
|
||||
List the stashes that you currently have. Each 'stash' is listed
|
||||
with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1} is
|
||||
with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1}` is
|
||||
the one before, etc.), the name of the branch that was current when the
|
||||
stash was made, and a short description of the commit the stash was
|
||||
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>]::
|
||||
@@ -58,7 +61,7 @@ show [<stash>]::
|
||||
stashed state and its original parent. When no `<stash>` is given,
|
||||
shows the latest one. By default, the command shows the diffstat, but
|
||||
it will accept any format known to `git-diff` (e.g., `git-stash show
|
||||
-p stash@\{2}` to view the second most recent stash in patch form).
|
||||
-p stash@\{1}` to view the second most recent stash in patch form).
|
||||
|
||||
apply [<stash>]::
|
||||
|
||||
|
||||
@@ -27,6 +27,13 @@ The command takes the same set of options as `git-commit`; it
|
||||
shows what would be committed if the same options are given to
|
||||
`git-commit`.
|
||||
|
||||
If any paths have been touched in the working tree (that is,
|
||||
their modification times have changed) but their contents and
|
||||
permissions are identical to those in the index file, the command
|
||||
updates the index file. Running `git-status` can thus speed up
|
||||
subsequent operations such as `git-diff` if the working tree
|
||||
contains many paths that have been touched but not modified.
|
||||
|
||||
|
||||
OUTPUT
|
||||
------
|
||||
|
||||
@@ -435,6 +435,26 @@ Tracking and contributing to an entire Subversion-managed project
|
||||
# of dcommit/rebase/show-ignore should be the same as above.
|
||||
------------------------------------------------------------------------
|
||||
|
||||
The initial 'git-svn clone' can be quite time-consuming
|
||||
(especially for large Subversion repositories). If multiple
|
||||
people (or one person with multiple machines) want to use
|
||||
git-svn to interact with the same Subversion repository, you can
|
||||
do the initial 'git-svn clone' to a repository on a server and
|
||||
have each person clone that repository with 'git clone':
|
||||
|
||||
------------------------------------------------------------------------
|
||||
# Do the initial import on a server
|
||||
ssh server "cd /pub && git-svn clone http://svn.foo.org/project
|
||||
# Clone locally
|
||||
git clone server:/pub/project
|
||||
# Tell git-svn which branch contains the Subversion commits
|
||||
git update-ref refs/remotes/git-svn origin/master
|
||||
# Initialize git-svn locally (be sure to use the same URL and -T/-b/-t options as were used on server)
|
||||
git-svn init http://svn.foo.org/project
|
||||
# Pull the latest changes from Subversion
|
||||
git-svn rebase
|
||||
------------------------------------------------------------------------
|
||||
|
||||
REBASE VS. PULL/MERGE
|
||||
---------------------
|
||||
|
||||
|
||||
@@ -417,7 +417,25 @@ 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_SSH'::
|
||||
If this environment variable is set then gitlink:git-fetch[1]
|
||||
and gitlink:git-push[1] will use this command instead
|
||||
of `ssh` when they need to connect to a remote system.
|
||||
The 'GIT_SSH' command will be given exactly two arguments:
|
||||
the 'username@host' (or just 'host') from the URL and the
|
||||
shell command to execute on that remote system.
|
||||
+
|
||||
To pass options to the program that you want to list in GIT_SSH
|
||||
you will need to wrap the program and options into a shell script,
|
||||
then set GIT_SSH to refer to the shell script.
|
||||
+
|
||||
Usually it is easier to configure any desired options through your
|
||||
personal `.ssh/config` file. Please consult your ssh documentation
|
||||
for further details.
|
||||
|
||||
'GIT_FLUSH'::
|
||||
If this environment variable is set to "1", then commands such
|
||||
|
||||
@@ -405,7 +405,7 @@ the attributes given to path `t/abc` are computed as follows:
|
||||
and `bar` attributes should be given to this path, so it
|
||||
leaves `foo` and `bar` unset. Attribute `baz` is set.
|
||||
|
||||
3. Finally it examines `$GIT_DIR/info/gitattributes`. This file
|
||||
3. Finally it examines `$GIT_DIR/info/attributes`. This file
|
||||
is used to override the in-tree settings. The first line is
|
||||
a match, and `foo` is set, `bar` is reverted to unspecified
|
||||
state, and `baz` is unset.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,7 +7,7 @@ mandir="$2"
|
||||
SUBDIRECTORY_OK=t
|
||||
USAGE='<refname> <target directory>'
|
||||
. git-sh-setup
|
||||
export GIT_DIR
|
||||
cd_to_toplevel
|
||||
|
||||
test -z "$mandir" && usage
|
||||
if ! git rev-parse --verify "$head^0" >/dev/null; then
|
||||
@@ -18,14 +18,14 @@ fi
|
||||
GIT_INDEX_FILE=`pwd`/.quick-doc.index
|
||||
export GIT_INDEX_FILE
|
||||
rm -f "$GIT_INDEX_FILE"
|
||||
trap 'rm -f "$GIT_INDEX_FILE"' 0
|
||||
|
||||
git read-tree $head
|
||||
git checkout-index -a -f --prefix="$mandir"/
|
||||
|
||||
if test -n "$GZ"; then
|
||||
cd "$mandir"
|
||||
for i in `git ls-tree -r --name-only $head`
|
||||
do
|
||||
gzip < $i > $i.gz && rm $i
|
||||
done
|
||||
git ls-tree -r --name-only $head |
|
||||
xargs printf "$mandir/%s\n" |
|
||||
xargs gzip -f
|
||||
fi
|
||||
rm -f "$GIT_INDEX_FILE"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Git User's Manual (for version 1.5.1 or newer)
|
||||
Git User's Manual (for version 1.5.3 or newer)
|
||||
______________________________________________
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -1079,6 +1079,11 @@ $ git diff HEAD # difference between HEAD and working tree; what
|
||||
$ git status # a brief per-file summary of the above.
|
||||
-------------------------------------------------
|
||||
|
||||
You can also use gitlink:git-gui[1] to create commits, view changes in
|
||||
the index and the working tree files, and individually select diff hunks
|
||||
for inclusion in the index (by right-clicking on the diff hunk and
|
||||
choosing "Stage Hunk For Commit").
|
||||
|
||||
[[creating-good-commit-messages]]
|
||||
Creating good commit messages
|
||||
-----------------------------
|
||||
@@ -1257,7 +1262,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 +1356,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
|
||||
@@ -1484,6 +1489,38 @@ $ git show HEAD^:path/to/file
|
||||
|
||||
which will display the given version of the file.
|
||||
|
||||
[[interrupted-work]]
|
||||
Temporarily setting aside work in progress
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
While you are in the middle of working on something complicated, you
|
||||
find an unrelated but obvious and trivial bug. You would like to fix it
|
||||
before continuing. You can use gitlink:git-stash[1] to save the current
|
||||
state of your work, and after fixing the bug (or, optionally after doing
|
||||
so on a different branch and then coming back), unstash the
|
||||
work-in-progress changes.
|
||||
|
||||
------------------------------------------------
|
||||
$ git stash "work in progress for foo feature"
|
||||
------------------------------------------------
|
||||
|
||||
This command will save your changes away to the `stash`, and
|
||||
reset your working tree and the index to match the tip of your
|
||||
current branch. Then you can make your fix as usual.
|
||||
|
||||
------------------------------------------------
|
||||
... edit and test ...
|
||||
$ git commit -a -m "blorpl: typofix"
|
||||
------------------------------------------------
|
||||
|
||||
After that, you can go back to what you were working on with
|
||||
`git stash apply`:
|
||||
|
||||
------------------------------------------------
|
||||
$ git stash apply
|
||||
------------------------------------------------
|
||||
|
||||
|
||||
[[ensuring-good-performance]]
|
||||
Ensuring good performance
|
||||
-------------------------
|
||||
@@ -1667,24 +1704,19 @@ one step:
|
||||
$ git pull origin master
|
||||
-------------------------------------------------
|
||||
|
||||
In fact, "origin" is normally the default repository to pull from,
|
||||
and the default branch is normally the HEAD of the remote repository,
|
||||
so often you can accomplish the above with just
|
||||
In fact, if you have "master" checked out, then by default "git pull"
|
||||
merges from the HEAD branch of the origin repository. So often you can
|
||||
accomplish the above with just a simple
|
||||
|
||||
-------------------------------------------------
|
||||
$ git pull
|
||||
-------------------------------------------------
|
||||
|
||||
See the descriptions of the branch.<name>.remote and branch.<name>.merge
|
||||
options in gitlink:git-config[1] to learn how to control these defaults
|
||||
depending on the current branch. Also note that the --track option to
|
||||
gitlink:git-branch[1] and gitlink:git-checkout[1] can be used to
|
||||
automatically set the default remote branch to pull from at the time
|
||||
that a branch is created:
|
||||
|
||||
-------------------------------------------------
|
||||
$ git checkout --track -b maint origin/maint
|
||||
-------------------------------------------------
|
||||
More generally, a branch that is created from a remote branch will pull
|
||||
by default from that branch. See the descriptions of the
|
||||
branch.<name>.remote and branch.<name>.merge options in
|
||||
gitlink:git-config[1], and the discussion of the --track option in
|
||||
gitlink:git-checkout[1], to learn how to control these defaults.
|
||||
|
||||
In addition to saving you keystrokes, "git pull" also helps you by
|
||||
producing a default commit message documenting the branch and
|
||||
@@ -2479,8 +2511,10 @@ $ gitk origin..mywork &
|
||||
|
||||
And browse through the list of patches in the mywork branch using gitk,
|
||||
applying them (possibly in a different order) to mywork-new using
|
||||
cherry-pick, and possibly modifying them as you go using commit
|
||||
--amend.
|
||||
cherry-pick, and possibly modifying them as you go using commit --amend.
|
||||
The git-gui[1] command may also help as it allows you to individually
|
||||
select diff hunks for inclusion in the index (by right-clicking on the
|
||||
diff hunk and choosing "Stage Hunk for Commit").
|
||||
|
||||
Another technique is to use git-format-patch to create a series of
|
||||
patches, then reset the state to before the patches:
|
||||
|
||||
11
INSTALL
11
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
|
||||
|
||||
@@ -117,3 +116,7 @@ Issues of note:
|
||||
would instead give you a copy of what you see at:
|
||||
|
||||
http://www.kernel.org/pub/software/scm/git/docs/
|
||||
|
||||
It has been reported that docbook-xsl version 1.72 and 1.73 are
|
||||
buggy; 1.72 misformats manual pages for callouts, and 1.73 needs
|
||||
the patch in contrib/patches/docbook-xsl-manpages-charmap.patch
|
||||
|
||||
27
Makefile
27
Makefile
@@ -151,6 +151,7 @@ sysconfdir = /etc
|
||||
else
|
||||
sysconfdir = $(prefix)/etc
|
||||
endif
|
||||
lib = lib
|
||||
ETC_GITCONFIG = $(sysconfdir)/gitconfig
|
||||
# DESTDIR=
|
||||
|
||||
@@ -176,7 +177,9 @@ CC = gcc
|
||||
AR = ar
|
||||
RM = rm -f
|
||||
TAR = tar
|
||||
FIND = find
|
||||
INSTALL = /bin/install
|
||||
INSTALL = install
|
||||
RPMBUILD = rpmbuild
|
||||
TCL_PATH = tclsh
|
||||
TCLTK_PATH = wish
|
||||
@@ -457,6 +460,10 @@ ifeq ($(uname_S),AIX)
|
||||
NO_STRLCPY = YesPlease
|
||||
NEEDS_LIBICONV=YesPlease
|
||||
endif
|
||||
ifeq ($(uname_S),GNU)
|
||||
# GNU/Hurd
|
||||
NO_STRLCPY=YesPlease
|
||||
endif
|
||||
ifeq ($(uname_S),IRIX64)
|
||||
NO_IPV6=YesPlease
|
||||
NO_SETENV=YesPlease
|
||||
@@ -528,9 +535,9 @@ endif
|
||||
|
||||
ifndef NO_CURL
|
||||
ifdef CURLDIR
|
||||
# Try "-Wl,-rpath=$(CURLDIR)/lib" in such a case.
|
||||
# Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
|
||||
BASIC_CFLAGS += -I$(CURLDIR)/include
|
||||
CURL_LIBCURL = -L$(CURLDIR)/lib $(CC_LD_DYNPATH)$(CURLDIR)/lib -lcurl
|
||||
CURL_LIBCURL = -L$(CURLDIR)/$(lib) $(CC_LD_DYNPATH)$(CURLDIR)/$(lib) -lcurl
|
||||
else
|
||||
CURL_LIBCURL = -lcurl
|
||||
endif
|
||||
@@ -546,11 +553,17 @@ ifndef NO_CURL
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef ZLIB_PATH
|
||||
BASIC_CFLAGS += -I$(ZLIB_PATH)/include
|
||||
EXTLIBS += -L$(ZLIB_PATH)/$(lib) $(CC_LD_DYNPATH)$(ZLIB_PATH)/$(lib)
|
||||
endif
|
||||
EXTLIBS += -lz
|
||||
|
||||
ifndef NO_OPENSSL
|
||||
OPENSSL_LIBSSL = -lssl
|
||||
ifdef OPENSSLDIR
|
||||
BASIC_CFLAGS += -I$(OPENSSLDIR)/include
|
||||
OPENSSL_LINK = -L$(OPENSSLDIR)/lib $(CC_LD_DYNPATH)$(OPENSSLDIR)/lib
|
||||
OPENSSL_LINK = -L$(OPENSSLDIR)/$(lib) $(CC_LD_DYNPATH)$(OPENSSLDIR)/$(lib)
|
||||
else
|
||||
OPENSSL_LINK =
|
||||
endif
|
||||
@@ -567,7 +580,7 @@ endif
|
||||
ifdef NEEDS_LIBICONV
|
||||
ifdef ICONVDIR
|
||||
BASIC_CFLAGS += -I$(ICONVDIR)/include
|
||||
ICONV_LINK = -L$(ICONVDIR)/lib $(CC_LD_DYNPATH)$(ICONVDIR)/lib
|
||||
ICONV_LINK = -L$(ICONVDIR)/$(lib) $(CC_LD_DYNPATH)$(ICONVDIR)/$(lib)
|
||||
else
|
||||
ICONV_LINK =
|
||||
endif
|
||||
@@ -933,11 +946,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)):\
|
||||
@@ -966,7 +979,7 @@ endif
|
||||
|
||||
### Testing rules
|
||||
|
||||
TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X
|
||||
TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X
|
||||
|
||||
all: $(TEST_PROGRAMS)
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -2508,7 +2508,7 @@ static void write_out_one_result(struct patch *patch, int phase)
|
||||
* thing: remove the old, write the new
|
||||
*/
|
||||
if (phase == 0)
|
||||
remove_file(patch, 0);
|
||||
remove_file(patch, patch->is_rename);
|
||||
if (phase == 1)
|
||||
create_file(patch);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "cache.h"
|
||||
|
||||
static const char git_config_set_usage[] =
|
||||
"git-config [ --global | --system ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list";
|
||||
"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list";
|
||||
|
||||
static char *key;
|
||||
static regex_t *key_regexp;
|
||||
@@ -186,6 +186,13 @@ int cmd_config(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
else if (!strcmp(argv[1], "--system"))
|
||||
setenv(CONFIG_ENVIRONMENT, git_etc_gitconfig(), 1);
|
||||
else if (!strcmp(argv[1], "--file") || !strcmp(argv[1], "-f")) {
|
||||
if (argc < 3)
|
||||
usage(git_config_set_usage);
|
||||
setenv(CONFIG_ENVIRONMENT, argv[2], 1);
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
else if (!strcmp(argv[1], "--null") || !strcmp(argv[1], "-z")) {
|
||||
term = '\0';
|
||||
delim = '\n';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -184,36 +184,7 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the full path to the working tree specified in $GIT_WORK_TREE
|
||||
* or NULL if no working tree is specified.
|
||||
*/
|
||||
static const char *get_work_tree(void)
|
||||
{
|
||||
const char *git_work_tree;
|
||||
char cwd[PATH_MAX];
|
||||
static char worktree[PATH_MAX];
|
||||
|
||||
git_work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
|
||||
if (!git_work_tree)
|
||||
return NULL;
|
||||
if (!getcwd(cwd, sizeof(cwd)))
|
||||
die("Unable to read current working directory");
|
||||
if (chdir(git_work_tree))
|
||||
die("Cannot change directory to specified working tree '%s'",
|
||||
git_work_tree);
|
||||
if (git_work_tree[0] != '/') {
|
||||
if (!getcwd(worktree, sizeof(worktree)))
|
||||
die("Unable to read current working directory");
|
||||
git_work_tree = worktree;
|
||||
}
|
||||
if (chdir(cwd))
|
||||
die("Cannot come back to cwd");
|
||||
return git_work_tree;
|
||||
}
|
||||
|
||||
static int create_default_files(const char *git_dir, const char *git_work_tree,
|
||||
const char *template_path)
|
||||
static int create_default_files(const char *git_dir, const char *template_path)
|
||||
{
|
||||
unsigned len = strlen(git_dir);
|
||||
static char path[PATH_MAX];
|
||||
@@ -292,16 +263,16 @@ static int create_default_files(const char *git_dir, const char *git_work_tree,
|
||||
}
|
||||
git_config_set("core.filemode", filemode ? "true" : "false");
|
||||
|
||||
if (is_bare_repository() && !git_work_tree) {
|
||||
if (is_bare_repository())
|
||||
git_config_set("core.bare", "true");
|
||||
}
|
||||
else {
|
||||
const char *work_tree = get_git_work_tree();
|
||||
git_config_set("core.bare", "false");
|
||||
/* allow template config file to override the default */
|
||||
if (log_all_ref_updates == -1)
|
||||
git_config_set("core.logallrefupdates", "true");
|
||||
if (git_work_tree)
|
||||
git_config_set("core.worktree", git_work_tree);
|
||||
if (work_tree != git_work_tree_cfg)
|
||||
git_config_set("core.worktree", work_tree);
|
||||
}
|
||||
return reinit;
|
||||
}
|
||||
@@ -318,7 +289,6 @@ static const char init_db_usage[] =
|
||||
int cmd_init_db(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
const char *git_dir;
|
||||
const char *git_work_tree;
|
||||
const char *sha1_dir;
|
||||
const char *template_dir = NULL;
|
||||
char *path;
|
||||
@@ -339,7 +309,11 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
|
||||
usage(init_db_usage);
|
||||
}
|
||||
|
||||
git_work_tree = get_work_tree();
|
||||
git_work_tree_cfg = xcalloc(PATH_MAX, 1);
|
||||
if (!getcwd(git_work_tree_cfg, PATH_MAX))
|
||||
die ("Cannot access current working directory.");
|
||||
if (access(get_git_work_tree(), X_OK))
|
||||
die ("Cannot access work tree '%s'", get_git_work_tree());
|
||||
|
||||
/*
|
||||
* Set up the default .git directory contents
|
||||
@@ -356,7 +330,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
|
||||
*/
|
||||
check_repository_format();
|
||||
|
||||
reinit = create_default_files(git_dir, git_work_tree, template_dir);
|
||||
reinit = create_default_files(git_dir, template_dir);
|
||||
|
||||
/*
|
||||
* And set up the object store.
|
||||
|
||||
@@ -469,9 +469,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
|
||||
break;
|
||||
}
|
||||
|
||||
if (require_work_tree &&
|
||||
(!is_inside_work_tree() || is_inside_git_dir()))
|
||||
die("This operation must be run in a work tree");
|
||||
if (require_work_tree && !is_inside_work_tree()) {
|
||||
const char *work_tree = get_git_work_tree();
|
||||
if (!work_tree || chdir(work_tree))
|
||||
die("This operation must be run in a work tree");
|
||||
}
|
||||
|
||||
pathspec = get_pathspec(prefix, argv + i);
|
||||
|
||||
|
||||
@@ -237,8 +237,6 @@ static int eatspace(char *line)
|
||||
|
||||
static char *cleanup_subject(char *subject)
|
||||
{
|
||||
if (keep_subject)
|
||||
return subject;
|
||||
for (;;) {
|
||||
char *p;
|
||||
int len, remove;
|
||||
@@ -425,6 +423,7 @@ static int read_one_header_line(char *line, int sz, FILE *in)
|
||||
if (addlen >= sz - len)
|
||||
addlen = sz - len - 1;
|
||||
memcpy(line + len, continuation, addlen);
|
||||
line[len] = '\n';
|
||||
len += addlen;
|
||||
}
|
||||
}
|
||||
@@ -499,15 +498,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);
|
||||
}
|
||||
@@ -819,6 +845,22 @@ static void handle_body(void)
|
||||
return;
|
||||
}
|
||||
|
||||
static void output_header_lines(FILE *fout, const char *hdr, char *data)
|
||||
{
|
||||
while (1) {
|
||||
char *ep = strchr(data, '\n');
|
||||
int len;
|
||||
if (!ep)
|
||||
len = strlen(data);
|
||||
else
|
||||
len = ep - data;
|
||||
fprintf(fout, "%s: %.*s\n", hdr, len, data);
|
||||
if (!ep)
|
||||
break;
|
||||
data = ep + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_info(void)
|
||||
{
|
||||
char *sub;
|
||||
@@ -836,9 +878,13 @@ static void handle_info(void)
|
||||
continue;
|
||||
|
||||
if (!memcmp(header[i], "Subject", 7)) {
|
||||
sub = cleanup_subject(hdr);
|
||||
cleanup_space(sub);
|
||||
fprintf(fout, "Subject: %s\n", sub);
|
||||
if (keep_subject)
|
||||
sub = hdr;
|
||||
else {
|
||||
sub = cleanup_subject(hdr);
|
||||
cleanup_space(sub);
|
||||
}
|
||||
output_header_lines(fout, "Subject", sub);
|
||||
} else if (!memcmp(header[i], "From", 4)) {
|
||||
handle_from(hdr);
|
||||
fprintf(fout, "Author: %s\n", name);
|
||||
|
||||
@@ -97,7 +97,6 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.head_idx = -1;
|
||||
|
||||
setup_git_directory();
|
||||
git_config(git_default_config);
|
||||
|
||||
newfd = hold_locked_index(&lock_file, 1);
|
||||
|
||||
@@ -300,7 +300,7 @@ static struct commit_list *find_bisection(struct commit_list *list,
|
||||
show_list("bisection 2 sorted", 0, nr, list);
|
||||
|
||||
*all = nr;
|
||||
weights = xcalloc(on_list, sizeof(int*));
|
||||
weights = xcalloc(on_list, sizeof(*weights));
|
||||
counted = 0;
|
||||
|
||||
for (n = 0, p = list; p; p = p->next) {
|
||||
|
||||
@@ -321,6 +321,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
if (!strcmp(arg, "--show-cdup")) {
|
||||
const char *pfx = prefix;
|
||||
if (!is_inside_work_tree()) {
|
||||
const char *work_tree =
|
||||
get_git_work_tree();
|
||||
if (work_tree)
|
||||
printf("%s\n", work_tree);
|
||||
continue;
|
||||
}
|
||||
while (pfx) {
|
||||
pfx = strchr(pfx, '/');
|
||||
if (pfx) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -43,8 +43,6 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
|
||||
msg = argv[1];
|
||||
if (!*msg)
|
||||
die("Refusing to perform update with empty message");
|
||||
if (strchr(msg, '\n'))
|
||||
die("Refusing to perform update with \\n in message");
|
||||
}
|
||||
else if (!strcmp("--", arg)) {
|
||||
argc--;
|
||||
|
||||
@@ -23,8 +23,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
|
||||
msg = argv[++i];
|
||||
if (!*msg)
|
||||
die("Refusing to perform update with empty message.");
|
||||
if (strchr(msg, '\n'))
|
||||
die("Refusing to perform update with \\n in message.");
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-d", argv[i])) {
|
||||
|
||||
@@ -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
|
||||
|
||||
10
cache.h
10
cache.h
@@ -208,12 +208,15 @@ enum object_type {
|
||||
extern int is_bare_repository_cfg;
|
||||
extern int is_bare_repository(void);
|
||||
extern int is_inside_git_dir(void);
|
||||
extern char *git_work_tree_cfg;
|
||||
extern int is_inside_work_tree(void);
|
||||
extern const char *get_git_dir(void);
|
||||
extern char *get_object_directory(void);
|
||||
extern char *get_refs_directory(void);
|
||||
extern char *get_index_file(void);
|
||||
extern char *get_graft_file(void);
|
||||
extern int set_git_dir(const char *path);
|
||||
extern const char *get_git_work_tree(void);
|
||||
|
||||
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
|
||||
|
||||
@@ -265,7 +268,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);
|
||||
@@ -361,7 +364,11 @@ int safe_create_leading_directories(char *path);
|
||||
char *enter_repo(char *path, int strict);
|
||||
static inline int is_absolute_path(const char *path)
|
||||
{
|
||||
#ifdef __MINGW32__
|
||||
return path[0] == '/' || (isalpha(path[0]) && path[1] == ':');
|
||||
#else
|
||||
return path[0] == '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int is_dev_null(const char *str)
|
||||
@@ -372,6 +379,7 @@ static inline int is_dev_null(const char *str)
|
||||
#endif
|
||||
return !strcmp(str, "/dev/null");
|
||||
}
|
||||
const char *make_absolute_path(const char *path);
|
||||
|
||||
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
|
||||
extern int sha1_object_info(const unsigned char *, unsigned long *);
|
||||
|
||||
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);
|
||||
|
||||
33
config.c
33
config.c
@@ -731,7 +731,7 @@ int git_config_set_multivar(const char* key, const char* value,
|
||||
int fd = -1, in_fd;
|
||||
int ret;
|
||||
char* config_filename;
|
||||
char* lock_file;
|
||||
struct lock_file *lock = NULL;
|
||||
const char* last_dot = strrchr(key, '.');
|
||||
|
||||
config_filename = getenv(CONFIG_ENVIRONMENT);
|
||||
@@ -741,7 +741,6 @@ int git_config_set_multivar(const char* key, const char* value,
|
||||
config_filename = git_path("config");
|
||||
}
|
||||
config_filename = xstrdup(config_filename);
|
||||
lock_file = xstrdup(mkpath("%s.lock", config_filename));
|
||||
|
||||
/*
|
||||
* Since "key" actually contains the section name and the real
|
||||
@@ -786,11 +785,12 @@ int git_config_set_multivar(const char* key, const char* value,
|
||||
store.key[i] = 0;
|
||||
|
||||
/*
|
||||
* The lock_file serves a purpose in addition to locking: the new
|
||||
* The lock serves a purpose in addition to locking: the new
|
||||
* contents of .git/config will be written into it.
|
||||
*/
|
||||
fd = open(lock_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
|
||||
if (fd < 0 || adjust_shared_perm(lock_file)) {
|
||||
lock = xcalloc(sizeof(struct lock_file), 1);
|
||||
fd = hold_lock_file_for_update(lock, config_filename, 0);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "could not lock config file\n");
|
||||
free(store.key);
|
||||
ret = -1;
|
||||
@@ -930,27 +930,32 @@ int git_config_set_multivar(const char* key, const char* value,
|
||||
goto write_err_out;
|
||||
|
||||
munmap(contents, contents_sz);
|
||||
unlink(config_filename);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
fd = -1;
|
||||
if (rename(lock_file, config_filename) < 0) {
|
||||
fprintf(stderr, "Could not rename the lock file?\n");
|
||||
if (close(fd) || commit_lock_file(lock) < 0) {
|
||||
fprintf(stderr, "Cannot commit config file!\n");
|
||||
fd =
|
||||
ret = 4;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* fd is closed, so don't try to close it below. */
|
||||
fd = -1;
|
||||
/*
|
||||
* lock is committed, so don't try to roll it back below.
|
||||
* NOTE: Since lockfile.c keeps a linked list of all created
|
||||
* lock_file structures, it isn't safe to free(lock). It's
|
||||
* better to just leave it hanging around.
|
||||
*/
|
||||
lock = NULL;
|
||||
ret = 0;
|
||||
|
||||
out_free:
|
||||
if (0 <= fd)
|
||||
close(fd);
|
||||
if (lock)
|
||||
rollback_lock_file(lock);
|
||||
free(config_filename);
|
||||
if (lock_file) {
|
||||
unlink(lock_file);
|
||||
free(lock_file);
|
||||
}
|
||||
return ret;
|
||||
|
||||
write_err_out:
|
||||
|
||||
14
configure.ac
14
configure.ac
@@ -69,12 +69,26 @@ fi \
|
||||
## Site configuration related to programs (before tests)
|
||||
## --with-PACKAGE[=ARG] and --without-PACKAGE
|
||||
#
|
||||
# Set lib to alternative name of lib directory (e.g. lib64)
|
||||
AC_ARG_WITH([lib],
|
||||
[AS_HELP_STRING([--with-lib=ARG],
|
||||
[ARG specifies alternative name for lib directory])],
|
||||
[if test "$withval" = "no" -o "$withval" = "yes"; then \
|
||||
AC_MSG_WARN([You should provide name for --with-lib=ARG]); \
|
||||
else \
|
||||
GIT_CONF_APPEND_LINE(lib=$withval); \
|
||||
fi; \
|
||||
],[])
|
||||
#
|
||||
# Define SHELL_PATH to provide path to shell.
|
||||
GIT_ARG_SET_PATH(shell)
|
||||
#
|
||||
# Define PERL_PATH to provide path to Perl.
|
||||
GIT_ARG_SET_PATH(perl)
|
||||
#
|
||||
# Define ZLIB_PATH to provide path to zlib.
|
||||
GIT_ARG_SET_PATH(zlib)
|
||||
#
|
||||
# Declare the with-tcltk/without-tcltk options.
|
||||
AC_ARG_WITH(tcltk,
|
||||
AS_HELP_STRING([--with-tcltk],[use Tcl/Tk GUI (default is YES)])
|
||||
|
||||
@@ -972,6 +972,11 @@ _git_show ()
|
||||
__git_complete_file
|
||||
}
|
||||
|
||||
_git_stash ()
|
||||
{
|
||||
__gitcomp 'list show apply clear'
|
||||
}
|
||||
|
||||
_git ()
|
||||
{
|
||||
local i c=1 command __git_dir
|
||||
@@ -1028,6 +1033,7 @@ _git ()
|
||||
shortlog) _git_shortlog ;;
|
||||
show) _git_show ;;
|
||||
show-branch) _git_log ;;
|
||||
stash) _git_stash ;;
|
||||
whatchanged) _git_log ;;
|
||||
*) COMPREPLY=() ;;
|
||||
esac
|
||||
@@ -1073,6 +1079,7 @@ complete -o default -o nospace -F _git_remote git-remote
|
||||
complete -o default -o nospace -F _git_reset git-reset
|
||||
complete -o default -o nospace -F _git_shortlog git-shortlog
|
||||
complete -o default -o nospace -F _git_show git-show
|
||||
complete -o default -o nospace -F _git_stash git-stash
|
||||
complete -o default -o nospace -F _git_log git-show-branch
|
||||
complete -o default -o nospace -F _git_log git-whatchanged
|
||||
|
||||
|
||||
@@ -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 (setf (ewoc-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,69 @@ 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-get-exclude-files ()
|
||||
"Get the list of exclude files to pass to git-ls-files."
|
||||
(let (files
|
||||
(config (git-config "core.excludesfile")))
|
||||
(when (file-readable-p ".git/info/exclude")
|
||||
(push ".git/info/exclude" files))
|
||||
(when (and config (file-readable-p config))
|
||||
(push config files))
|
||||
files))
|
||||
|
||||
(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 (or (not files) remaining-files)
|
||||
(let ((exclude-files (git-get-exclude-files)))
|
||||
(setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o"
|
||||
(concat "--exclude-per-directory=" git-per-dir-ignore-file)
|
||||
(mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
|
||||
; 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 +842,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 +882,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 +1068,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 +1101,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 = {}
|
||||
@@ -812,16 +839,20 @@ class P4Sync(Command):
|
||||
if file["action"] == "delete":
|
||||
self.gitStream.write("D %s\n" % relPath)
|
||||
else:
|
||||
mode = 644
|
||||
if file["type"].startswith("x"):
|
||||
mode = 755
|
||||
|
||||
data = file['data']
|
||||
|
||||
mode = "644"
|
||||
if file["type"].startswith("x"):
|
||||
mode = "755"
|
||||
elif file["type"] == "symlink":
|
||||
mode = "120000"
|
||||
# p4 print on a symlink contains "target\n", so strip it off
|
||||
data = data[:-1]
|
||||
|
||||
if self.isWindows and file["type"].endswith("text"):
|
||||
data = data.replace("\r\n", "\n")
|
||||
|
||||
self.gitStream.write("M %d inline %s\n" % (mode, relPath))
|
||||
self.gitStream.write("M %s inline %s\n" % (mode, relPath))
|
||||
self.gitStream.write("data %s\n" % len(data))
|
||||
self.gitStream.write(data)
|
||||
self.gitStream.write("\n")
|
||||
@@ -874,7 +905,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 +1011,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:
|
||||
@@ -1169,11 +1185,11 @@ class P4Sync(Command):
|
||||
elif ',' not in self.changeRange:
|
||||
self.revision = self.changeRange
|
||||
self.changeRange = ""
|
||||
p = p[0:atIdx]
|
||||
p = p[:atIdx]
|
||||
elif p.find("#") != -1:
|
||||
hashIdx = p.index("#")
|
||||
self.revision = p[hashIdx:]
|
||||
p = p[0:hashIdx]
|
||||
p = p[:hashIdx]
|
||||
elif self.previousDepotPaths == []:
|
||||
self.revision = "#head"
|
||||
|
||||
@@ -1284,10 +1300,10 @@ class P4Sync(Command):
|
||||
changeNum = line.split(" ")[1]
|
||||
changes.append(changeNum)
|
||||
|
||||
changes.reverse()
|
||||
changes.sort()
|
||||
|
||||
if len(self.maxChanges) > 0:
|
||||
changes = changes[0:min(int(self.maxChanges), len(changes))]
|
||||
changes = changes[:min(int(self.maxChanges), len(changes))]
|
||||
|
||||
if len(changes) == 0:
|
||||
if not self.silent:
|
||||
|
||||
21
contrib/patches/docbook-xsl-manpages-charmap.patch
Normal file
21
contrib/patches/docbook-xsl-manpages-charmap.patch
Normal file
@@ -0,0 +1,21 @@
|
||||
From: Ismail Dönmez <ismail@pardus.org.tr>
|
||||
|
||||
Trying to build the documentation with docbook-xsl 1.73 may result in
|
||||
the following error. This patch fixes it.
|
||||
|
||||
$ xmlto -m callouts.xsl man git-add.xml
|
||||
runtime error: file
|
||||
file:///usr/share/sgml/docbook/xsl-stylesheets-1.73.0/manpages/other.xsl line
|
||||
129 element call-template
|
||||
The called template 'read-character-map' was not found.
|
||||
|
||||
--- docbook-xsl-1.73.0/manpages/docbook.xsl.manpages-charmap 2007-07-23 16:24:23.000000000 +0100
|
||||
+++ docbook-xsl-1.73.0/manpages/docbook.xsl 2007-07-23 16:25:16.000000000 +0100
|
||||
@@ -37,6 +37,7 @@
|
||||
<xsl:include href="lists.xsl"/>
|
||||
<xsl:include href="endnotes.xsl"/>
|
||||
<xsl:include href="table.xsl"/>
|
||||
+ <xsl:include href="../common/charmap.xsl"/>
|
||||
|
||||
<!-- * we rename the following just to avoid using params with "man" -->
|
||||
<!-- * prefixes in the table.xsl stylesheet (because that stylesheet -->
|
||||
26
daemon.c
26
daemon.c
@@ -16,7 +16,8 @@ static int reuseaddr;
|
||||
static const char daemon_usage[] =
|
||||
"git-daemon [--verbose] [--syslog] [--export-all]\n"
|
||||
" [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
|
||||
" [--base-path=path] [--user-path | --user-path=path]\n"
|
||||
" [--base-path=path] [--base-path-relaxed]\n"
|
||||
" [--user-path | --user-path=path]\n"
|
||||
" [--interpolated-path=path]\n"
|
||||
" [--reuseaddr] [--detach] [--pid-file=file]\n"
|
||||
" [--[enable|disable|allow-override|forbid-override]=service]\n"
|
||||
@@ -34,6 +35,7 @@ static int export_all_trees;
|
||||
/* Take all paths relative to this one if non-NULL */
|
||||
static char *base_path;
|
||||
static char *interpolated_path;
|
||||
static int base_path_relaxed;
|
||||
|
||||
/* Flag indicating client sent extra args. */
|
||||
static int saw_extended_args;
|
||||
@@ -180,6 +182,7 @@ static char *path_ok(struct interp *itable)
|
||||
{
|
||||
static char rpath[PATH_MAX];
|
||||
static char interp_path[PATH_MAX];
|
||||
int retried_path = 0;
|
||||
char *path;
|
||||
char *dir;
|
||||
|
||||
@@ -235,7 +238,22 @@ static char *path_ok(struct interp *itable)
|
||||
dir = rpath;
|
||||
}
|
||||
|
||||
path = enter_repo(dir, strict_paths);
|
||||
do {
|
||||
path = enter_repo(dir, strict_paths);
|
||||
if (path)
|
||||
break;
|
||||
|
||||
/*
|
||||
* if we fail and base_path_relaxed is enabled, try without
|
||||
* prefixing the base path
|
||||
*/
|
||||
if (base_path && base_path_relaxed && !retried_path) {
|
||||
dir = itable[INTERP_SLOT_DIR].value;
|
||||
retried_path = 1;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
} while (1);
|
||||
|
||||
if (!path) {
|
||||
logerror("'%s': unable to chdir or not a git archive", dir);
|
||||
@@ -1061,6 +1079,10 @@ int main(int argc, char **argv)
|
||||
base_path = arg+12;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--base-path-relaxed")) {
|
||||
base_path_relaxed = 1;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--interpolated-path=")) {
|
||||
interpolated_path = arg+20;
|
||||
continue;
|
||||
|
||||
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);
|
||||
|
||||
43
dir.c
43
dir.c
@@ -642,3 +642,46 @@ file_exists(const char *f)
|
||||
struct stat sb;
|
||||
return stat(f, &sb) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_relative_cwd() gets the prefix of the current working directory
|
||||
* relative to 'dir'. If we are not inside 'dir', it returns NULL.
|
||||
*
|
||||
* As a convenience, it also returns NULL if 'dir' is already NULL. The
|
||||
* reason for this behaviour is that it is natural for functions returning
|
||||
* directory names to return NULL to say "this directory does not exist"
|
||||
* or "this directory is invalid". These cases are usually handled the
|
||||
* same as if the cwd is not inside 'dir' at all, so get_relative_cwd()
|
||||
* returns NULL for both of them.
|
||||
*
|
||||
* Most notably, get_relative_cwd(buffer, size, get_git_work_tree())
|
||||
* unifies the handling of "outside work tree" with "no work tree at all".
|
||||
*/
|
||||
char *get_relative_cwd(char *buffer, int size, const char *dir)
|
||||
{
|
||||
char *cwd = buffer;
|
||||
|
||||
if (!dir)
|
||||
return NULL;
|
||||
if (!getcwd(buffer, size))
|
||||
die("can't find the current directory: %s", strerror(errno));
|
||||
|
||||
if (!is_absolute_path(dir))
|
||||
dir = make_absolute_path(dir);
|
||||
|
||||
while (*dir && *dir == *cwd) {
|
||||
dir++;
|
||||
cwd++;
|
||||
}
|
||||
if (*dir)
|
||||
return NULL;
|
||||
if (*cwd == '/')
|
||||
return cwd + 1;
|
||||
return cwd;
|
||||
}
|
||||
|
||||
int is_inside_dir(const char *dir)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
|
||||
}
|
||||
|
||||
3
dir.h
3
dir.h
@@ -61,4 +61,7 @@ extern void add_exclude(const char *string, const char *base,
|
||||
extern int file_exists(const char *);
|
||||
extern struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len);
|
||||
|
||||
extern char *get_relative_cwd(char *buffer, int size, const char *dir);
|
||||
extern int is_inside_dir(const char *dir);
|
||||
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,10 @@ int pager_in_use;
|
||||
int pager_use_color = 1;
|
||||
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
|
||||
|
||||
/* This is set by setup_git_dir_gently() and/or git_default_config() */
|
||||
char *git_work_tree_cfg;
|
||||
static const char *work_tree;
|
||||
|
||||
static const char *git_dir;
|
||||
static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
|
||||
|
||||
@@ -62,15 +66,8 @@ static void setup_git_env(void)
|
||||
|
||||
int is_bare_repository(void)
|
||||
{
|
||||
const char *dir, *s;
|
||||
if (0 <= is_bare_repository_cfg)
|
||||
return is_bare_repository_cfg;
|
||||
|
||||
dir = get_git_dir();
|
||||
if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))
|
||||
return 0;
|
||||
s = strrchr(dir, '/');
|
||||
return !s || strcmp(s + 1, DEFAULT_GIT_DIR_ENVIRONMENT);
|
||||
/* if core.bare is not 'false', let's see if there is a work tree */
|
||||
return is_bare_repository_cfg && !get_git_work_tree();
|
||||
}
|
||||
|
||||
const char *get_git_dir(void)
|
||||
@@ -80,6 +77,26 @@ const char *get_git_dir(void)
|
||||
return git_dir;
|
||||
}
|
||||
|
||||
const char *get_git_work_tree(void)
|
||||
{
|
||||
static int initialized = 0;
|
||||
if (!initialized) {
|
||||
work_tree = getenv(GIT_WORK_TREE_ENVIRONMENT);
|
||||
/* core.bare = true overrides implicit and config work tree */
|
||||
if (!work_tree && is_bare_repository_cfg < 1) {
|
||||
work_tree = git_work_tree_cfg;
|
||||
/* make_absolute_path also normalizes the path */
|
||||
if (work_tree && !is_absolute_path(work_tree))
|
||||
work_tree = xstrdup(make_absolute_path(git_path(work_tree)));
|
||||
} else if (work_tree)
|
||||
work_tree = xstrdup(make_absolute_path(work_tree));
|
||||
initialized = 1;
|
||||
if (work_tree)
|
||||
is_bare_repository_cfg = 0;
|
||||
}
|
||||
return work_tree;
|
||||
}
|
||||
|
||||
char *get_object_directory(void)
|
||||
{
|
||||
if (!git_object_dir)
|
||||
@@ -107,3 +124,11 @@ char *get_graft_file(void)
|
||||
setup_git_env();
|
||||
return git_graft_file;
|
||||
}
|
||||
|
||||
int set_git_dir(const char *path)
|
||||
{
|
||||
if (setenv(GIT_DIR_ENVIRONMENT, path, 1))
|
||||
return error("Could not set GIT_DIR to '%s'", path);
|
||||
setup_git_env();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,16 @@
|
||||
use strict;
|
||||
|
||||
sub run_cmd_pipe {
|
||||
my $fh = undef;
|
||||
open($fh, '-|', @_) or die;
|
||||
return <$fh>;
|
||||
if ($^O eq 'MSWin32') {
|
||||
my @invalid = grep {m/[":*]/} @_;
|
||||
die "$^O does not support: @invalid\n" if @invalid;
|
||||
my @args = map { m/ /o ? "\"$_\"": $_ } @_;
|
||||
return qx{@args};
|
||||
} else {
|
||||
my $fh = undef;
|
||||
open($fh, '-|', @_) or die;
|
||||
return <$fh>;
|
||||
}
|
||||
}
|
||||
|
||||
my ($GIT_DIR) = run_cmd_pipe(qw(git rev-parse --git-dir));
|
||||
@@ -17,7 +24,7 @@ chomp($GIT_DIR);
|
||||
|
||||
sub refresh {
|
||||
my $fh;
|
||||
open $fh, '-|', qw(git update-index --refresh)
|
||||
open $fh, 'git update-index --refresh |'
|
||||
or die;
|
||||
while (<$fh>) {
|
||||
;# ignore 'needs update'
|
||||
@@ -296,7 +303,7 @@ sub revert_cmd {
|
||||
my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
|
||||
map { $_->{VALUE} } @update);
|
||||
my $fh;
|
||||
open $fh, '|-', qw(git update-index --index-info)
|
||||
open $fh, '| git update-index --index-info'
|
||||
or die;
|
||||
for (@lines) {
|
||||
print $fh $_;
|
||||
@@ -725,7 +732,7 @@ sub patch_update_cmd {
|
||||
if (@result) {
|
||||
my $fh;
|
||||
|
||||
open $fh, '|-', qw(git apply --cached);
|
||||
open $fh, '| git apply --cached';
|
||||
for (@{$head->{TEXT}}, @result) {
|
||||
print $fh $_;
|
||||
}
|
||||
|
||||
11
git-am.sh
11
git-am.sh
@@ -103,7 +103,8 @@ It does not apply to blobs recorded in its index."
|
||||
}
|
||||
|
||||
prec=4
|
||||
dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= resolvemsg=
|
||||
dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary=
|
||||
resolvemsg= resume=
|
||||
git_apply_opt=
|
||||
|
||||
while case "$#" in 0) break;; esac
|
||||
@@ -284,6 +285,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 +371,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
|
||||
@@ -189,7 +190,6 @@ $1"
|
||||
;;
|
||||
--a|--am|--ame|--amen|--amend)
|
||||
amend=t
|
||||
log_given=t$log_given
|
||||
use_commit=HEAD
|
||||
shift
|
||||
;;
|
||||
@@ -248,6 +248,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
|
||||
@@ -290,9 +297,9 @@ esac
|
||||
|
||||
case "$log_given" in
|
||||
tt*)
|
||||
die "Only one of -c/-C/-F/--amend can be used." ;;
|
||||
die "Only one of -c/-C/-F can be used." ;;
|
||||
*tm*|*mt*)
|
||||
die "Option -m cannot be combined with -c/-C/-F/--amend." ;;
|
||||
die "Option -m cannot be combined with -c/-C/-F." ;;
|
||||
esac
|
||||
|
||||
case "$#,$also,$only,$amend" in
|
||||
@@ -321,6 +328,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 +469,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 +562,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 +590,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,10 +544,35 @@ 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
|
||||
|
||||
@@ -602,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
|
||||
@@ -626,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]
|
||||
|
||||
######################################################################
|
||||
##
|
||||
@@ -759,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]} {
|
||||
@@ -801,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 {}
|
||||
@@ -828,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
|
||||
@@ -1220,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
|
||||
|
||||
@@ -1346,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
|
||||
@@ -1354,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.
|
||||
@@ -1567,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
|
||||
@@ -1633,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
|
||||
@@ -1767,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 \
|
||||
@@ -1806,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
|
||||
@@ -1845,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
|
||||
@@ -1939,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
|
||||
@@ -1980,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 {
|
||||
@@ -2000,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 -
|
||||
@@ -2116,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 \
|
||||
@@ -2390,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
|
||||
#
|
||||
@@ -2461,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
|
||||
@@ -2552,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,12 @@ do
|
||||
|
||||
require_clean_work_tree
|
||||
|
||||
if [ ! -z "$2"]
|
||||
mkdir "$DOTEST" || die "Could not create temporary $DOTEST"
|
||||
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
|
||||
|
||||
@@ -362,7 +419,6 @@ do
|
||||
|
||||
test -z "$ONTO" && ONTO=$UPSTREAM
|
||||
|
||||
mkdir "$DOTEST" || die "Could not create temporary $DOTEST"
|
||||
: > "$DOTEST"/interactive || die "Could not mark as interactive"
|
||||
git symbolic-ref HEAD > "$DOTEST"/head-name ||
|
||||
die "Could not get HEAD"
|
||||
@@ -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
|
||||
@@ -407,20 +463,21 @@ do
|
||||
#
|
||||
EOF
|
||||
git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
|
||||
--abbrev=7 --reverse $UPSTREAM..$HEAD | \
|
||||
sed "s/^/pick /" >> "$TODO"
|
||||
--abbrev=7 --reverse --left-right --cherry-pick \
|
||||
$UPSTREAM...$HEAD | \
|
||||
sed -n "s/^>/pick /p" >> "$TODO"
|
||||
|
||||
test -z "$(grep -ve '^$' -e '^#' < $TODO)" &&
|
||||
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>) {
|
||||
@@ -289,7 +289,7 @@ sub expand_aliases {
|
||||
}
|
||||
|
||||
@to = expand_aliases(@to);
|
||||
@to = (map { sanitize_address_rfc822($_) } @to);
|
||||
@to = (map { sanitize_address($_) } @to);
|
||||
@initial_cc = expand_aliases(@initial_cc);
|
||||
@bcclist = expand_aliases(@bcclist);
|
||||
|
||||
@@ -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")
|
||||
@@ -460,22 +459,41 @@ sub unquote_rfc2047 {
|
||||
return "$_";
|
||||
}
|
||||
|
||||
# If an address contains a . in the name portion, the name must be quoted.
|
||||
sub sanitize_address_rfc822
|
||||
# use the simplest quoting being able to handle the recipient
|
||||
sub sanitize_address
|
||||
{
|
||||
my ($recipient) = @_;
|
||||
my ($recipient_name) = ($recipient =~ /^(.*?)\s+</);
|
||||
if ($recipient_name && $recipient_name =~ /\./ && $recipient_name !~ /^".*"$/) {
|
||||
my ($name, $addr) = ($recipient =~ /^(.*?)(\s+<.*)/);
|
||||
$recipient = "\"$name\"$addr";
|
||||
my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
|
||||
|
||||
if (not $recipient_name) {
|
||||
return "$recipient";
|
||||
}
|
||||
return $recipient;
|
||||
|
||||
# if recipient_name is already quoted, do nothing
|
||||
if ($recipient_name =~ /^(".*"|=\?utf-8\?q\?.*\?=)$/) {
|
||||
return $recipient;
|
||||
}
|
||||
|
||||
# rfc2047 is needed if a non-ascii char is included
|
||||
if ($recipient_name =~ /[^[:ascii:]]/) {
|
||||
$recipient_name =~ s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg;
|
||||
$recipient_name =~ s/(.*)/=\?utf-8\?q\?$1\?=/;
|
||||
}
|
||||
|
||||
# double quotes are needed if specials or CTLs are included
|
||||
elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) {
|
||||
$recipient_name =~ s/(["\\\r])/\\$1/;
|
||||
$recipient_name = "\"$recipient_name\"";
|
||||
}
|
||||
|
||||
return "$recipient_name $recipient_addr";
|
||||
|
||||
}
|
||||
|
||||
sub send_message
|
||||
{
|
||||
my @recipients = unique_email_list(@to);
|
||||
@cc = (map { sanitize_address_rfc822($_) } @cc);
|
||||
@cc = (map { sanitize_address($_) } @cc);
|
||||
my $to = join (",\n\t", @recipients);
|
||||
@recipients = unique_email_list(@recipients,@cc,@bcclist);
|
||||
@recipients = (map { extract_valid_address($_) } @recipients);
|
||||
@@ -490,7 +508,7 @@ sub send_message
|
||||
if ($cc ne '') {
|
||||
$ccline = "\nCc: $cc";
|
||||
}
|
||||
$from = sanitize_address_rfc822($from);
|
||||
$from = sanitize_address($from);
|
||||
make_message_id();
|
||||
|
||||
my $header = "From: $from
|
||||
|
||||
@@ -28,6 +28,22 @@ set_reflog_action() {
|
||||
fi
|
||||
}
|
||||
|
||||
git_editor() {
|
||||
: "${GIT_EDITOR:=$(git config core.editor)}"
|
||||
: "${GIT_EDITOR:=${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
|
||||
eval "${GIT_EDITOR:=vi}" '"$@"'
|
||||
}
|
||||
|
||||
is_bare_repository () {
|
||||
git rev-parse --is-bare-repository
|
||||
}
|
||||
@@ -44,8 +60,7 @@ cd_to_toplevel () {
|
||||
}
|
||||
|
||||
require_work_tree () {
|
||||
test $(git rev-parse --is-inside-work-tree) = true &&
|
||||
test $(git rev-parse --is-inside-git-dir) = false ||
|
||||
test $(git rev-parse --is-inside-work-tree) = true ||
|
||||
die "fatal: $0 cannot be used without a working tree."
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
44
git-svn.perl
44
git-svn.perl
@@ -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;
|
||||
@@ -938,8 +938,8 @@ sub resolve_local_globs {
|
||||
foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) {
|
||||
next unless m#^refs/remotes/$ref->{regex}$#;
|
||||
my $p = $1;
|
||||
my $pathname = $path->full_path($p);
|
||||
my $refname = $ref->full_path($p);
|
||||
my $pathname = desanitize_refname($path->full_path($p));
|
||||
my $refname = desanitize_refname($ref->full_path($p));
|
||||
if (my $existing = $fetch->{$pathname}) {
|
||||
if ($existing ne $refname) {
|
||||
die "Refspec conflict:\n",
|
||||
@@ -1239,7 +1239,40 @@ sub new {
|
||||
$self;
|
||||
}
|
||||
|
||||
sub refname { "refs/remotes/$_[0]->{ref_id}" }
|
||||
sub refname {
|
||||
my ($refname) = "refs/remotes/$_[0]->{ref_id}" ;
|
||||
|
||||
# It cannot end with a slash /, we'll throw up on this because
|
||||
# SVN can't have directories with a slash in their name, either:
|
||||
if ($refname =~ m{/$}) {
|
||||
die "ref: '$refname' ends with a trailing slash, this is ",
|
||||
"not permitted by git nor Subversion\n";
|
||||
}
|
||||
|
||||
# It cannot have ASCII control character space, tilde ~, caret ^,
|
||||
# colon :, question-mark ?, asterisk *, space, or open bracket [
|
||||
# anywhere.
|
||||
#
|
||||
# Additionally, % must be escaped because it is used for escaping
|
||||
# and we want our escaped refname to be reversible
|
||||
$refname =~ s{([ \%~\^:\?\*\[\t])}{uc sprintf('%%%02x',ord($1))}eg;
|
||||
|
||||
# no slash-separated component can begin with a dot .
|
||||
# /.* becomes /%2E*
|
||||
$refname =~ s{/\.}{/%2E}g;
|
||||
|
||||
# It cannot have two consecutive dots .. anywhere
|
||||
# .. becomes %2E%2E
|
||||
$refname =~ s{\.\.}{%2E%2E}g;
|
||||
|
||||
return $refname;
|
||||
}
|
||||
|
||||
sub desanitize_refname {
|
||||
my ($refname) = @_;
|
||||
$refname =~ s{%(?:([0-9A-F]{2}))}{chr hex($1)}eg;
|
||||
return $refname;
|
||||
}
|
||||
|
||||
sub svm_uuid {
|
||||
my ($self) = @_;
|
||||
@@ -2724,6 +2757,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
|
||||
|
||||
24
git.c
24
git.c
@@ -276,9 +276,14 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv)
|
||||
prefix = setup_git_directory();
|
||||
if (p->option & USE_PAGER)
|
||||
setup_pager();
|
||||
if ((p->option & NEED_WORK_TREE) &&
|
||||
(!is_inside_work_tree() || is_inside_git_dir()))
|
||||
die("%s must be run in a work tree", p->cmd);
|
||||
if (p->option & NEED_WORK_TREE) {
|
||||
const char *work_tree = get_git_work_tree();
|
||||
const char *git_dir = get_git_dir();
|
||||
if (!is_absolute_path(git_dir))
|
||||
set_git_dir(make_absolute_path(git_dir));
|
||||
if (!work_tree || chdir(work_tree))
|
||||
die("%s must be run in a work tree", p->cmd);
|
||||
}
|
||||
trace_argv_printf(argv, argc, "trace: built-in: git");
|
||||
|
||||
status = p->fn(argc, argv, prefix);
|
||||
@@ -314,7 +319,8 @@ static void handle_internal_command(int argc, const char **argv)
|
||||
{ "branch", cmd_branch, RUN_SETUP },
|
||||
{ "bundle", cmd_bundle },
|
||||
{ "cat-file", cmd_cat_file, RUN_SETUP },
|
||||
{ "checkout-index", cmd_checkout_index, RUN_SETUP },
|
||||
{ "checkout-index", cmd_checkout_index,
|
||||
RUN_SETUP | NEED_WORK_TREE},
|
||||
{ "check-ref-format", cmd_check_ref_format },
|
||||
{ "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "cherry", cmd_cherry, RUN_SETUP },
|
||||
@@ -466,11 +472,11 @@ int main(int argc, const char **argv)
|
||||
cmd = argv[0];
|
||||
|
||||
/*
|
||||
* We search for git commands in the following order:
|
||||
* - git_exec_path()
|
||||
* - the path of the "git" command if we could find it
|
||||
* in $0
|
||||
* - the regular PATH.
|
||||
* We execute external git command via execv_git_cmd(),
|
||||
* which looks at "--exec-path" option, GIT_EXEC_PATH
|
||||
* environment, and $(gitexecdir) in Makefile while built,
|
||||
* in this order. For scripted commands, we prepend
|
||||
* the value of the exec_path variable to the PATH.
|
||||
*/
|
||||
if (exec_path)
|
||||
prepend_to_path(exec_path, strlen(exec_path));
|
||||
|
||||
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
|
||||
|
||||
@@ -1410,6 +1515,7 @@ sub git_get_projects_list {
|
||||
|
||||
File::Find::find({
|
||||
follow_fast => 1, # follow symbolic links
|
||||
follow_skip => 2, # ignore duplicates
|
||||
dangling_symlinks => 0, # ignore dangling symlinks, silently
|
||||
wanted => sub {
|
||||
# skip project-list toplevel, if we get it.
|
||||
@@ -2185,9 +2291,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 +2739,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 +3449,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 +3475,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 +4259,6 @@ sub git_blob {
|
||||
}
|
||||
|
||||
sub git_tree {
|
||||
my $have_snapshot = gitweb_have_snapshot();
|
||||
|
||||
if (!defined $hash_base) {
|
||||
$hash_base = "HEAD";
|
||||
}
|
||||
@@ -4167,11 +4292,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 +4359,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 +4406,6 @@ sub git_snapshot {
|
||||
print <$fd>;
|
||||
binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
|
||||
close $fd;
|
||||
|
||||
}
|
||||
|
||||
sub git_log {
|
||||
@@ -4390,8 +4524,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 +4562,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";
|
||||
@@ -5234,7 +5366,7 @@ sub git_feed {
|
||||
|
||||
# log/feed of current (HEAD) branch, log of given branch, history of file/directory
|
||||
my $head = $hash || 'HEAD';
|
||||
my @commitlist = parse_commits($head, 150);
|
||||
my @commitlist = parse_commits($head, 150, 0, undef, $file_name);
|
||||
|
||||
my %latest_commit;
|
||||
my %latest_date;
|
||||
|
||||
103
lockfile.c
103
lockfile.c
@@ -27,9 +27,110 @@ static void remove_lock_file_on_signal(int signo)
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
/*
|
||||
* p = absolute or relative path name
|
||||
*
|
||||
* Return a pointer into p showing the beginning of the last path name
|
||||
* element. If p is empty or the root directory ("/"), just return p.
|
||||
*/
|
||||
static char *last_path_elm(char *p)
|
||||
{
|
||||
/* r starts pointing to null at the end of the string */
|
||||
char *r = strchr(p, '\0');
|
||||
|
||||
if (r == p)
|
||||
return p; /* just return empty string */
|
||||
|
||||
r--; /* back up to last non-null character */
|
||||
|
||||
/* back up past trailing slashes, if any */
|
||||
while (r > p && *r == '/')
|
||||
r--;
|
||||
|
||||
/*
|
||||
* then go backwards until I hit a slash, or the beginning of
|
||||
* the string
|
||||
*/
|
||||
while (r > p && *(r-1) != '/')
|
||||
r--;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/* We allow "recursive" symbolic links. Only within reason, though */
|
||||
#define MAXDEPTH 5
|
||||
|
||||
/*
|
||||
* p = path that may be a symlink
|
||||
* s = full size of p
|
||||
*
|
||||
* If p is a symlink, attempt to overwrite p with a path to the real
|
||||
* file or directory (which may or may not exist), following a chain of
|
||||
* symlinks if necessary. Otherwise, leave p unmodified.
|
||||
*
|
||||
* This is a best-effort routine. If an error occurs, p will either be
|
||||
* left unmodified or will name a different symlink in a symlink chain
|
||||
* that started with p's initial contents.
|
||||
*
|
||||
* Always returns p.
|
||||
*/
|
||||
|
||||
static char *resolve_symlink(char *p, size_t s)
|
||||
{
|
||||
int depth = MAXDEPTH;
|
||||
|
||||
while (depth--) {
|
||||
char link[PATH_MAX];
|
||||
int link_len = readlink(p, link, sizeof(link));
|
||||
if (link_len < 0) {
|
||||
/* not a symlink anymore */
|
||||
return p;
|
||||
}
|
||||
else if (link_len < sizeof(link))
|
||||
/* readlink() never null-terminates */
|
||||
link[link_len] = '\0';
|
||||
else {
|
||||
warning("%s: symlink too long", p);
|
||||
return p;
|
||||
}
|
||||
|
||||
if (link[0] == '/') {
|
||||
/* absolute path simply replaces p */
|
||||
if (link_len < s)
|
||||
strcpy(p, link);
|
||||
else {
|
||||
warning("%s: symlink too long", p);
|
||||
return p;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* link is a relative path, so I must replace the
|
||||
* last element of p with it.
|
||||
*/
|
||||
char *r = (char*)last_path_elm(p);
|
||||
if (r - p + link_len < s)
|
||||
strcpy(r, link);
|
||||
else {
|
||||
warning("%s: symlink too long", p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
static int lock_file(struct lock_file *lk, const char *path)
|
||||
{
|
||||
sprintf(lk->filename, "%s.lock", path);
|
||||
|
||||
if (strlen(path) >= sizeof(lk->filename)) return -1;
|
||||
strcpy(lk->filename, path);
|
||||
/*
|
||||
* subtract 5 from size to make sure there's room for adding
|
||||
* ".lock" for the lock file name
|
||||
*/
|
||||
resolve_symlink(lk->filename, sizeof(lk->filename)-5);
|
||||
strcat(lk->filename, ".lock");
|
||||
lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
|
||||
if (0 <= lk->fd) {
|
||||
if (!lock_file_list) {
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
5
pager.c
5
pager.c
@@ -44,8 +44,11 @@ void setup_pager(void)
|
||||
|
||||
if (!isatty(1))
|
||||
return;
|
||||
if (!pager)
|
||||
if (!pager) {
|
||||
if (!pager_program)
|
||||
git_config(git_default_config);
|
||||
pager = pager_program;
|
||||
}
|
||||
if (!pager)
|
||||
pager = getenv("PAGER");
|
||||
if (!pager)
|
||||
|
||||
96
path.c
96
path.c
@@ -71,24 +71,23 @@ 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");
|
||||
#ifdef __MINGW32__
|
||||
if (!tmp)
|
||||
tmp = getenv("TMP");
|
||||
if (!tmp)
|
||||
tmp = getenv("TEMP");
|
||||
#endif
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -306,3 +305,68 @@ int adjust_shared_perm(const char *path)
|
||||
return -2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We allow "recursive" symbolic links. Only within reason, though. */
|
||||
#define MAXDEPTH 5
|
||||
|
||||
const char *make_absolute_path(const char *path)
|
||||
{
|
||||
static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
|
||||
char cwd[1024] = "";
|
||||
int buf_index = 1, len;
|
||||
|
||||
int depth = MAXDEPTH;
|
||||
char *last_elem = NULL;
|
||||
struct stat st;
|
||||
|
||||
if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
|
||||
die ("Too long path: %.*s", 60, path);
|
||||
|
||||
while (depth--) {
|
||||
if (stat(buf, &st) || !S_ISDIR(st.st_mode)) {
|
||||
char *last_slash = strrchr(buf, '/');
|
||||
if (last_slash) {
|
||||
*last_slash = '\0';
|
||||
last_elem = xstrdup(last_slash + 1);
|
||||
} else
|
||||
last_elem = xstrdup(buf);
|
||||
}
|
||||
|
||||
if (*buf) {
|
||||
if (!*cwd && !getcwd(cwd, sizeof(cwd)))
|
||||
die ("Could not get current working directory");
|
||||
|
||||
if (chdir(buf))
|
||||
die ("Could not switch to '%s'", buf);
|
||||
}
|
||||
if (!getcwd(buf, PATH_MAX))
|
||||
die ("Could not get current working directory");
|
||||
|
||||
if (last_elem) {
|
||||
int len = strlen(buf);
|
||||
if (len + strlen(last_elem) + 2 > PATH_MAX)
|
||||
die ("Too long path name: '%s/%s'",
|
||||
buf, last_elem);
|
||||
buf[len] = '/';
|
||||
strcpy(buf + len + 1, last_elem);
|
||||
free(last_elem);
|
||||
last_elem = NULL;
|
||||
}
|
||||
|
||||
if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
|
||||
len = readlink(buf, next_buf, PATH_MAX);
|
||||
if (len < 0)
|
||||
die ("Invalid symlink: %s", buf);
|
||||
next_buf[len] = '\0';
|
||||
buf = next_buf;
|
||||
buf_index = 1 - buf_index;
|
||||
next_buf = bufs[buf_index];
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (*cwd && chdir(cwd))
|
||||
die ("Could not change back to '%s'", cwd);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
11
read-cache.c
11
read-cache.c
@@ -380,7 +380,7 @@ static int index_name_pos_also_unmerged(struct index_state *istate,
|
||||
|
||||
int add_file_to_index(struct index_state *istate, const char *path, int verbose)
|
||||
{
|
||||
int size, namelen;
|
||||
int size, namelen, pos;
|
||||
struct stat st;
|
||||
struct cache_entry *ce;
|
||||
|
||||
@@ -414,6 +414,15 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
|
||||
ce->ce_mode = ce_mode_from_stat(ent, st.st_mode);
|
||||
}
|
||||
|
||||
pos = index_name_pos(istate, ce->name, namelen);
|
||||
if (0 <= pos &&
|
||||
!ce_stage(istate->cache[pos]) &&
|
||||
!ie_modified(istate, istate->cache[pos], &st, 1)) {
|
||||
/* Nothing changed, really */
|
||||
free(ce);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (index_path(ce->sha1, path, &st, 1))
|
||||
die("unable to index file %s", path);
|
||||
if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
|
||||
|
||||
@@ -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);
|
||||
|
||||
44
refs.c
44
refs.c
@@ -1045,6 +1045,32 @@ void unlock_ref(struct ref_lock *lock)
|
||||
free(lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* copy the reflog message msg to buf, which has been allocated sufficiently
|
||||
* large, while cleaning up the whitespaces. Especially, convert LF to space,
|
||||
* because reflog file is one line per entry.
|
||||
*/
|
||||
static int copy_msg(char *buf, const char *msg)
|
||||
{
|
||||
char *cp = buf;
|
||||
char c;
|
||||
int wasspace = 1;
|
||||
|
||||
*cp++ = '\t';
|
||||
while ((c = *msg++)) {
|
||||
if (wasspace && isspace(c))
|
||||
continue;
|
||||
wasspace = isspace(c);
|
||||
if (wasspace)
|
||||
c = ' ';
|
||||
*cp++ = c;
|
||||
}
|
||||
while (buf < cp && isspace(cp[-1]))
|
||||
cp--;
|
||||
*cp++ = '\n';
|
||||
return cp - buf;
|
||||
}
|
||||
|
||||
static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
|
||||
const unsigned char *new_sha1, const char *msg)
|
||||
{
|
||||
@@ -1098,21 +1124,7 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
|
||||
|
||||
adjust_shared_perm(log_file);
|
||||
|
||||
msglen = 0;
|
||||
if (msg) {
|
||||
/* clean up the message and make sure it is a single line */
|
||||
for ( ; *msg; msg++)
|
||||
if (!isspace(*msg))
|
||||
break;
|
||||
if (*msg) {
|
||||
const char *ep = strchr(msg, '\n');
|
||||
if (ep)
|
||||
msglen = ep - msg;
|
||||
else
|
||||
msglen = strlen(msg);
|
||||
}
|
||||
}
|
||||
|
||||
msglen = msg ? strlen(msg) : 0;
|
||||
committer = git_committer_info(-1);
|
||||
maxlen = strlen(committer) + msglen + 100;
|
||||
logrec = xmalloc(maxlen);
|
||||
@@ -1121,7 +1133,7 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
|
||||
sha1_to_hex(new_sha1),
|
||||
committer);
|
||||
if (msglen)
|
||||
len += sprintf(logrec + len - 1, "\t%.*s\n", msglen, msg) - 1;
|
||||
len += copy_msg(logrec + len - 1, msg) - 1;
|
||||
written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
|
||||
free(logrec);
|
||||
if (close(logfd) != 0 || written != len)
|
||||
|
||||
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 */
|
||||
|
||||
303
setup.c
303
setup.c
@@ -1,4 +1,8 @@
|
||||
#include "cache.h"
|
||||
#include "dir.h"
|
||||
|
||||
static int inside_git_dir = -1;
|
||||
static int inside_work_tree = -1;
|
||||
|
||||
#ifdef __MINGW32__
|
||||
static inline int is_dir_sep(char c) { return c == '/' || c == '\\'; }
|
||||
@@ -140,7 +144,7 @@ void verify_non_filename(const char *prefix, const char *arg)
|
||||
if (!lstat(name, &st))
|
||||
die("ambiguous argument '%s': both revision and filename\n"
|
||||
"Use '--' to separate filenames from revisions", arg);
|
||||
if (errno != ENOENT)
|
||||
if (errno != ENOENT && errno != ENOTDIR)
|
||||
die("'%s': %s", arg, strerror(errno));
|
||||
}
|
||||
|
||||
@@ -207,105 +211,95 @@ static int is_git_directory(const char *suspect)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int inside_git_dir = -1;
|
||||
|
||||
int is_inside_git_dir(void)
|
||||
{
|
||||
if (inside_git_dir >= 0)
|
||||
return inside_git_dir;
|
||||
die("BUG: is_inside_git_dir called before setup_git_directory");
|
||||
if (inside_git_dir < 0)
|
||||
inside_git_dir = is_inside_dir(get_git_dir());
|
||||
return inside_git_dir;
|
||||
}
|
||||
|
||||
static int inside_work_tree = -1;
|
||||
|
||||
int is_inside_work_tree(void)
|
||||
{
|
||||
if (inside_git_dir >= 0)
|
||||
return inside_work_tree;
|
||||
die("BUG: is_inside_work_tree called before setup_git_directory");
|
||||
if (inside_work_tree < 0)
|
||||
inside_work_tree = is_inside_dir(get_git_work_tree());
|
||||
return inside_work_tree;
|
||||
}
|
||||
|
||||
static char *gitworktree_config;
|
||||
|
||||
static int git_setup_config(const char *var, const char *value)
|
||||
/*
|
||||
* If no worktree was given, and we are outside of a default work tree,
|
||||
* now is the time to set it.
|
||||
*
|
||||
* In other words, if the user calls git with something like
|
||||
*
|
||||
* git --git-dir=/some/where/else/.git bla
|
||||
*
|
||||
* default to /some/where/else as working directory; if the specified
|
||||
* git-dir does not end in "/.git", the cwd is used as working directory.
|
||||
*/
|
||||
const char *set_work_tree(const char *dir)
|
||||
{
|
||||
if (!strcmp(var, "core.worktree")) {
|
||||
if (gitworktree_config)
|
||||
strlcpy(gitworktree_config, value, PATH_MAX);
|
||||
return 0;
|
||||
char dir_buffer[PATH_MAX], *rel = NULL;
|
||||
static char buffer[PATH_MAX + 1];
|
||||
int len, suffix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1;
|
||||
|
||||
/* strip the variable 'dir' of the postfix "/.git" if it has it */
|
||||
len = strlen(dir);
|
||||
if (len > suffix_len &&
|
||||
!strcmp(dir + len - suffix_len, "/" DEFAULT_GIT_DIR_ENVIRONMENT)) {
|
||||
if ((len - suffix_len) >= sizeof(dir_buffer))
|
||||
die("directory name too long");
|
||||
memcpy(dir_buffer, dir, len - suffix_len);
|
||||
dir_buffer[len - suffix_len] = '\0';
|
||||
|
||||
/* are we inside the default work tree? */
|
||||
rel = get_relative_cwd(buffer, sizeof(buffer), dir_buffer);
|
||||
}
|
||||
return git_default_config(var, value);
|
||||
|
||||
/* if rel is set, the cwd is _not_ the current working tree */
|
||||
if (rel && *rel) {
|
||||
if (!is_absolute_path(dir))
|
||||
set_git_dir(make_absolute_path(dir));
|
||||
dir = dir_buffer;
|
||||
if (chdir(dir))
|
||||
die("cannot chdir to %s: %s", dir, strerror(errno));
|
||||
else
|
||||
strcat(rel, "/");
|
||||
inside_git_dir = 0;
|
||||
} else {
|
||||
rel = NULL;
|
||||
dir = getcwd(buffer, sizeof(buffer));
|
||||
}
|
||||
git_work_tree_cfg = xstrdup(dir);
|
||||
inside_work_tree = 1;
|
||||
|
||||
return rel;
|
||||
}
|
||||
|
||||
/*
|
||||
* We cannot decide in this function whether we are in the work tree or
|
||||
* not, since the config can only be read _after_ this function was called.
|
||||
*/
|
||||
const char *setup_git_directory_gently(int *nongit_ok)
|
||||
{
|
||||
const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
|
||||
static char cwd[PATH_MAX+1];
|
||||
char worktree[PATH_MAX+1], gitdir[PATH_MAX+1];
|
||||
const char *gitdirenv, *gitworktree;
|
||||
int wt_rel_gitdir = 0;
|
||||
const char *gitdirenv;
|
||||
int len, offset, minoffset = 0;
|
||||
|
||||
/*
|
||||
* If GIT_DIR is set explicitly, we're not going
|
||||
* to do any discovery, but we still do repository
|
||||
* validation.
|
||||
*/
|
||||
gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
|
||||
if (!gitdirenv) {
|
||||
int len, offset;
|
||||
int minoffset = 0;
|
||||
|
||||
if (!getcwd(cwd, sizeof(cwd)-1))
|
||||
die("Unable to read current working directory");
|
||||
#ifdef __MINGW32__
|
||||
if (cwd[1] == ':')
|
||||
minoffset = 2;
|
||||
#endif
|
||||
|
||||
offset = len = strlen(cwd);
|
||||
for (;;) {
|
||||
if (is_git_directory(".git"))
|
||||
break;
|
||||
if (offset == minoffset) {
|
||||
offset = -1;
|
||||
break;
|
||||
}
|
||||
chdir("..");
|
||||
while (offset > minoffset && cwd[--offset] != '/')
|
||||
; /* do nothing */
|
||||
}
|
||||
|
||||
if (offset >= 0) {
|
||||
inside_work_tree = 1;
|
||||
git_config(git_default_config);
|
||||
if (offset == len) {
|
||||
inside_git_dir = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cwd[len++] = '/';
|
||||
cwd[len] = '\0';
|
||||
inside_git_dir = !prefixcmp(cwd + offset + 1, ".git/");
|
||||
return cwd + offset + 1;
|
||||
}
|
||||
|
||||
if (chdir(cwd))
|
||||
die("Cannot come back to cwd");
|
||||
if (!is_git_directory(".")) {
|
||||
if (nongit_ok) {
|
||||
*nongit_ok = 1;
|
||||
return NULL;
|
||||
}
|
||||
die("Not a git repository");
|
||||
}
|
||||
setenv(GIT_DIR_ENVIRONMENT, cwd, 1);
|
||||
gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
|
||||
if (!gitdirenv)
|
||||
die("getenv after setenv failed");
|
||||
}
|
||||
|
||||
if (PATH_MAX - 40 < strlen(gitdirenv)) {
|
||||
if (nongit_ok) {
|
||||
*nongit_ok = 1;
|
||||
if (gitdirenv) {
|
||||
if (PATH_MAX - 40 < strlen(gitdirenv))
|
||||
die("'$%s' too big", GIT_DIR_ENVIRONMENT);
|
||||
if (is_git_directory(gitdirenv)) {
|
||||
if (!work_tree_env)
|
||||
return set_work_tree(gitdirenv);
|
||||
return NULL;
|
||||
}
|
||||
die("$%s too big", GIT_DIR_ENVIRONMENT);
|
||||
}
|
||||
if (!is_git_directory(gitdirenv)) {
|
||||
if (nongit_ok) {
|
||||
*nongit_ok = 1;
|
||||
return NULL;
|
||||
@@ -315,92 +309,58 @@ const char *setup_git_directory_gently(int *nongit_ok)
|
||||
|
||||
if (!getcwd(cwd, sizeof(cwd)-1))
|
||||
die("Unable to read current working directory");
|
||||
if (chdir(gitdirenv)) {
|
||||
if (nongit_ok) {
|
||||
*nongit_ok = 1;
|
||||
return NULL;
|
||||
}
|
||||
die("Cannot change directory to $%s '%s'",
|
||||
GIT_DIR_ENVIRONMENT, gitdirenv);
|
||||
}
|
||||
if (!getcwd(gitdir, sizeof(gitdir)-1))
|
||||
die("Unable to read current working directory");
|
||||
if (chdir(cwd))
|
||||
die("Cannot come back to cwd");
|
||||
|
||||
#ifdef __MINGW32__
|
||||
if (cwd[1] == ':')
|
||||
minoffset = 2;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* In case there is a work tree we may change the directory,
|
||||
* therefore make GIT_DIR an absolute path.
|
||||
* Test in the following order (relative to the cwd):
|
||||
* - .git/
|
||||
* - ./ (bare)
|
||||
* - ../.git/
|
||||
* - ../ (bare)
|
||||
* - ../../.git/
|
||||
* etc.
|
||||
*/
|
||||
if (!is_absolute_path(gitdirenv)) {
|
||||
setenv(GIT_DIR_ENVIRONMENT, gitdir, 1);
|
||||
gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
|
||||
if (!gitdirenv)
|
||||
die("getenv after setenv failed");
|
||||
if (PATH_MAX - 40 < strlen(gitdirenv)) {
|
||||
if (nongit_ok) {
|
||||
*nongit_ok = 1;
|
||||
return NULL;
|
||||
}
|
||||
die("$%s too big after expansion to absolute path",
|
||||
GIT_DIR_ENVIRONMENT);
|
||||
}
|
||||
}
|
||||
|
||||
strcat(cwd, "/");
|
||||
strcat(gitdir, "/");
|
||||
inside_git_dir = !prefixcmp(cwd, gitdir);
|
||||
|
||||
gitworktree = getenv(GIT_WORK_TREE_ENVIRONMENT);
|
||||
if (!gitworktree) {
|
||||
gitworktree_config = worktree;
|
||||
worktree[0] = '\0';
|
||||
}
|
||||
git_config(git_setup_config);
|
||||
if (!gitworktree) {
|
||||
gitworktree_config = NULL;
|
||||
if (worktree[0])
|
||||
gitworktree = worktree;
|
||||
if (gitworktree && gitworktree[0] != '/')
|
||||
wt_rel_gitdir = 1;
|
||||
}
|
||||
|
||||
if (wt_rel_gitdir && chdir(gitdirenv))
|
||||
die("Cannot change directory to $%s '%s'",
|
||||
GIT_DIR_ENVIRONMENT, gitdirenv);
|
||||
if (gitworktree && chdir(gitworktree)) {
|
||||
if (nongit_ok) {
|
||||
if (wt_rel_gitdir && chdir(cwd))
|
||||
die("Cannot come back to cwd");
|
||||
*nongit_ok = 1;
|
||||
offset = len = strlen(cwd);
|
||||
for (;;) {
|
||||
if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
|
||||
break;
|
||||
if (is_git_directory(".")) {
|
||||
inside_git_dir = 1;
|
||||
if (!work_tree_env)
|
||||
inside_work_tree = 0;
|
||||
setenv(GIT_DIR_ENVIRONMENT, ".", 1);
|
||||
return NULL;
|
||||
}
|
||||
if (wt_rel_gitdir)
|
||||
die("Cannot change directory to working tree '%s'"
|
||||
" from $%s", gitworktree, GIT_DIR_ENVIRONMENT);
|
||||
else
|
||||
die("Cannot change directory to working tree '%s'",
|
||||
gitworktree);
|
||||
}
|
||||
if (!getcwd(worktree, sizeof(worktree)-1))
|
||||
die("Unable to read current working directory");
|
||||
strcat(worktree, "/");
|
||||
inside_work_tree = !prefixcmp(cwd, worktree);
|
||||
|
||||
if (gitworktree && inside_work_tree && !prefixcmp(worktree, gitdir) &&
|
||||
strcmp(worktree, gitdir)) {
|
||||
inside_git_dir = 0;
|
||||
chdir("..");
|
||||
do {
|
||||
if (offset == minoffset) {
|
||||
if (nongit_ok) {
|
||||
if (chdir(cwd))
|
||||
die("Cannot come back to cwd");
|
||||
*nongit_ok = 1;
|
||||
return NULL;
|
||||
}
|
||||
die("Not a git repository");
|
||||
}
|
||||
} while (cwd[--offset] != '/');
|
||||
}
|
||||
|
||||
if (!inside_work_tree) {
|
||||
if (chdir(cwd))
|
||||
die("Cannot come back to cwd");
|
||||
inside_git_dir = 0;
|
||||
if (!work_tree_env)
|
||||
inside_work_tree = 1;
|
||||
git_work_tree_cfg = xstrndup(cwd, offset);
|
||||
if (offset == len)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!strcmp(cwd, worktree))
|
||||
return NULL;
|
||||
return cwd+strlen(worktree);
|
||||
/* Make "offset" point to past the '/', and add a '/' at the end */
|
||||
offset++;
|
||||
cwd[len++] = '/';
|
||||
cwd[len] = 0;
|
||||
return cwd + offset;
|
||||
}
|
||||
|
||||
int git_config_perm(const char *var, const char *value)
|
||||
@@ -424,11 +384,21 @@ int git_config_perm(const char *var, const char *value)
|
||||
|
||||
int check_repository_format_version(const char *var, const char *value)
|
||||
{
|
||||
if (strcmp(var, "core.repositoryformatversion") == 0)
|
||||
repository_format_version = git_config_int(var, value);
|
||||
if (strcmp(var, "core.repositoryformatversion") == 0)
|
||||
repository_format_version = git_config_int(var, value);
|
||||
else if (strcmp(var, "core.sharedrepository") == 0)
|
||||
shared_repository = git_config_perm(var, value);
|
||||
return 0;
|
||||
else if (strcmp(var, "core.bare") == 0) {
|
||||
is_bare_repository_cfg = git_config_bool(var, value);
|
||||
if (is_bare_repository_cfg == 1)
|
||||
inside_work_tree = -1;
|
||||
} else if (strcmp(var, "core.worktree") == 0) {
|
||||
if (git_work_tree_cfg)
|
||||
free(git_work_tree_cfg);
|
||||
git_work_tree_cfg = xstrdup(value);
|
||||
inside_work_tree = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_repository_format(void)
|
||||
@@ -444,5 +414,16 @@ const char *setup_git_directory(void)
|
||||
{
|
||||
const char *retval = setup_git_directory_gently(NULL);
|
||||
check_repository_format();
|
||||
|
||||
/* If the work tree is not the default one, recompute prefix */
|
||||
if (inside_work_tree < 0) {
|
||||
static char buffer[PATH_MAX + 1];
|
||||
char *rel;
|
||||
if (retval && chdir(retval))
|
||||
die ("Could not jump back into original cwd");
|
||||
rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
|
||||
return rel && *rel ? strcat(rel, "/") : NULL;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user