Merge commit 'b8de7f764e1a9f6e8dfb587a6145906394fa607d'

This commit is contained in:
Johannes Sixt
2007-08-06 21:56:58 +02:00
83 changed files with 3026 additions and 1098 deletions

View File

@@ -131,7 +131,7 @@ clean:
user-manual.xml: user-manual.txt user-manual.conf
$(ASCIIDOC) -b docbook -d book $<
XSLT = http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl
XSLT = docbook.xsl
XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
user-manual.html: user-manual.xml

View File

@@ -32,8 +32,9 @@ Updates since v1.5.2
- "git rebase" learned an "interactive" mode that let you
pick and reorder which commits to rebuild.
- "git fsck" can save its findings in $GIT_DIR/lost-found,
without a separate invocation of "git lost-found" command.
- "git fsck" can save its findings in $GIT_DIR/lost-found, without a
separate invocation of "git lost-found" command. The blobs stored by
lost-found are stored in plain format to allow you to grep in them.
- $GIT_WORK_TREE environment variable can be used together with
$GIT_DIR to work in a subdirectory of a working tree that is
@@ -49,6 +50,10 @@ Updates since v1.5.2
- "git-cvsserver" learned new options (--base-path, --export-all,
--strict-paths) inspired by git-daemon.
- "git-commit" can use "-t templatefile" option and commit.template
configuration variable to prime the commit message given to you in the
editor.
- "git-submodule" command helps you manage the projects from
the superproject that contain them.
@@ -105,9 +110,30 @@ Updates since v1.5.2
* Updated behavior of existing commands.
- "gitweb" can offer multiple snapshot formats.
***NOTE*** Unfortunately, this changes the format of the
$feature{snapshot}{default} entry in the per-site
configuration file 'gitweb_config.perl'. It used to be a
three-element tuple that describe a single format; with the
new configuration item format, you only have to say the name
of the format ('tgz', 'tbz2' or 'zip'). Please update the
your configuration file accordingly.
- The editor to use with many interactive commands can be
overridden with GIT_EDITOR environment variable, or if it
does not exist, with core.editor configuration variable. As
before, if you have neither, environment variables VISUAL
and EDITOR are consulted in this order, and then finally we
fall back on "vi".
- "git rm --cached" does not complain when removing a newly
added file from the index anymore.
- Options to "git log" to affect how --grep/--author options look for
given strings now have shorter abbreviations. -i is for ignore case,
and -E is for extended regexp.
- "git svn dcommit" retains local merge information.
- "git config" to set values also honors type flags like --bool
@@ -189,6 +215,10 @@ Updates since v1.5.2
git-fast-import (also in contrib). The man page and p4
rpm have been removed as well.
- "git mailinfo" (hence "am") now tries to see if the message
is in utf-8 first, instead of assuming iso-8859-1, if
incoming e-mail does not say what encoding it is in.
* Builds
- old-style function definitions (most notably, a function
@@ -232,6 +262,6 @@ this release, unless otherwise noted.
--
exec >/var/tmp/1
O=v1.5.3-rc2
O=v1.5.3-rc3
echo O=`git describe refs/heads/master`
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint

View File

@@ -27,7 +27,13 @@ ifdef::backend-docbook[]
[listingblock]
<example><title>{title}</title>
<literallayout>
ifdef::doctype-manpage[]
&#10;.ft C&#10;
endif::doctype-manpage[]
|
ifdef::doctype-manpage[]
&#10;.ft&#10;
endif::doctype-manpage[]
</literallayout>
{title#}</example>
endif::backend-docbook[]

View File

@@ -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'.

View File

@@ -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.

View 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>

View File

@@ -130,8 +130,8 @@ Delete unneeded branch::
------------
$ git clone git://git.kernel.org/.../git.git my.git
$ cd my.git
$ git branch -d -r todo html man <1>
$ git branch -D test <2>
$ git branch -d -r origin/todo origin/html origin/man <1>
$ git branch -D test <2>
------------
+
<1> delete remote-tracking branches "todo", "html", "man"

View File

@@ -74,6 +74,13 @@ OPTIONS
-m <msg>|--message=<msg>::
Use the given <msg> as the commit message.
-t <file>|--template=<file>::
Use the contents of the given file as the initial version
of the commit message. The editor is invoked and you can
make subsequent changes. If a message is specified using
the `-m` or `-F` options, this option has no effect. This
overrides the `commit.template` configuration variable.
-s|--signoff::
Add Signed-off-by line at the end of the commit message.
@@ -244,10 +251,12 @@ on the Subject: line and the rest of the commit in the body.
include::i18n.txt[]
ENVIRONMENT VARIABLES
---------------------
The command specified by either the VISUAL or EDITOR environment
variables is used to edit the commit log message.
ENVIRONMENT AND CONFIGURATION VARIABLES
---------------------------------------
The editor used to edit the commit log message will be chosen from the
GIT_EDITOR environment variable, the core.editor configuration variable, the
VISUAL environment variable, or the EDITOR environment variable (in that
order).
HOOKS
-----

View File

@@ -102,17 +102,14 @@ Limiting the diff output::
+
------------
$ git diff --diff-filter=MRC <1>
$ git diff --name-status -r <2>
$ git diff --name-status <2>
$ git diff arch/i386 include/asm-i386 <3>
------------
+
<1> show only modification, rename and copy, but not addition
nor deletion.
<2> show only names and the nature of change, but not actual
diff output. --name-status disables usual patch generation
which in turn also disables recursive behavior, so without -r
you would only see the directory name if there is a change in a
file in a subdirectory.
diff output.
<3> limit diff output to named subtrees.
Munging the diff output::

View File

@@ -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
---------------------------------------------------------------

View File

@@ -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

View File

@@ -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[]

View File

@@ -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

View File

@@ -8,7 +8,8 @@ git-stash - Stash the changes in a dirty working directory away
SYNOPSIS
--------
[verse]
'git-stash' (save | list | show [<stash>] | apply [<stash>] | clear)
'git-stash' (list | show [<stash>] | apply [<stash>] | clear)
'git-stash' [save] [message...]
DESCRIPTION
-----------
@@ -22,7 +23,9 @@ The modifications stashed away by this command can be listed with
`git-stash list`, inspected with `git-stash show`, and restored
(potentially on top of a different commit) with `git-stash apply`.
Calling git-stash without any arguments is equivalent to `git-stash
save`.
save`. A stash is by default listed as "WIP on 'branchname' ...", but
you can give a more descriptive message on the command line when
you create one.
The latest stash you created is stored in `$GIT_DIR/refs/stash`; older
stashes are found in the reflog of this reference and can be named using
@@ -48,8 +51,8 @@ list::
based on.
+
----------------------------------------------------------------
stash@{0}: submit: 6ebd0e2... Add git-stash
stash@{1}: master: 9cc0589... Merge branch 'master' of gfi
stash@{0}: WIP on submit: 6ebd0e2... Update git-stash documentation
stash@{1}: On master: 9cc0589... Add git-stash
----------------------------------------------------------------
show [<stash>]::

View File

@@ -417,7 +417,9 @@ other
See gitlink:git-merge[1]
'GIT_PAGER'::
This environment variable overrides `$PAGER`.
This environment variable overrides `$PAGER`. If it is set
to an empty string or to the value "cat", git will not launch
a pager.
'GIT_FLUSH'::
If this environment variable is set to "1", then commands such

View File

@@ -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

View 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

View File

@@ -449,7 +449,7 @@ Exploring git history
Git is best thought of as a tool for storing the history of a
collection of files. It does this by storing compressed snapshots of
the contents of a file heirarchy, together with "commits" which show
the contents of a file hierarchy, together with "commits" which show
the relationships between these snapshots.
Git provides extremely flexible and fast tools for exploring the
@@ -1070,7 +1070,7 @@ about to commit:
-------------------------------------------------
$ git diff --cached # difference between HEAD and the index; what
# would be commited if you ran "commit" now.
# would be committed if you ran "commit" now.
$ git diff # difference between the index file and your
# working directory; changes that would not
# be included if you ran "commit" now.
@@ -1257,7 +1257,7 @@ index 802992c,2b60207..0000000
++>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
-------------------------------------------------
Recall that the commit which will be commited after we resolve this
Recall that the commit which will be committed after we resolve this
conflict will have two parents instead of the usual one: one parent
will be HEAD, the tip of the current branch; the other will be the
tip of the other branch, which is stored temporarily in MERGE_HEAD.
@@ -1351,7 +1351,7 @@ away, you can always return to the pre-merge state with
$ git reset --hard HEAD
-------------------------------------------------
Or, if you've already commited the merge that you want to throw away,
Or, if you've already committed the merge that you want to throw away,
-------------------------------------------------
$ git reset --hard ORIG_HEAD

View File

@@ -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

View File

@@ -176,6 +176,7 @@ CC = gcc
AR = ar
RM = rm -f
TAR = tar
FIND = find
INSTALL = install
RPMBUILD = rpmbuild
TCL_PATH = tclsh
@@ -934,11 +935,11 @@ doc:
TAGS:
$(RM) TAGS
find . -name '*.[hcS]' -print | xargs etags -a
$(FIND) . -name '*.[hcS]' -print | xargs etags -a
tags:
$(RM) tags
find . -name '*.[hcS]' -print | xargs ctags -a
$(FIND) . -name '*.[hcS]' -print | xargs ctags -a
### Detect prefix changes
TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\

View File

@@ -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);
}

View File

@@ -233,6 +233,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
die("diff_setup_done failed");
}
rev.diffopt.allow_external = 1;
rev.diffopt.recursive = 1;
/* Do we have --cached and not have a pending object, then
* default to HEAD by hand. Eek.

View File

@@ -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;

View File

@@ -499,15 +499,42 @@ static int decode_b_segment(char *in, char *ot, char *ep)
return 0;
}
/*
* When there is no known charset, guess.
*
* Right now we assume that if the target is UTF-8 (the default),
* and it already looks like UTF-8 (which includes US-ASCII as its
* subset, of course) then that is what it is and there is nothing
* to do.
*
* Otherwise, we default to assuming it is Latin1 for historical
* reasons.
*/
static const char *guess_charset(const char *line, const char *target_charset)
{
if (is_encoding_utf8(target_charset)) {
if (is_utf8(line))
return NULL;
}
return "latin1";
}
static void convert_to_utf8(char *line, const char *charset)
{
static const char latin_one[] = "latin1";
const char *input_charset = *charset ? charset : latin_one;
char *out = reencode_string(line, metainfo_charset, input_charset);
char *out;
if (!charset || !*charset) {
charset = guess_charset(line, metainfo_charset);
if (!charset)
return;
}
if (!strcmp(metainfo_charset, charset))
return;
out = reencode_string(line, metainfo_charset, charset);
if (!out)
die("cannot convert from %s to %s\n",
input_charset, metainfo_charset);
charset, metainfo_charset);
strcpy(line, out);
free(out);
}

View File

@@ -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);

View File

@@ -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

View File

@@ -265,7 +265,7 @@ extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat
extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
extern int read_pipe(int fd, char** return_buf, unsigned long* return_size);
extern int read_fd(int fd, char **return_buf, unsigned long *return_size);
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);

View File

@@ -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);

View File

@@ -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 $<

View File

@@ -314,8 +314,8 @@ and returns the process output as a string."
(sort-lines nil (point-min) (point-max))
(save-buffer))
(when created
(git-run-command nil nil "update-index" "--info-only" "--add" "--" (file-relative-name ignore-name)))
(git-add-status-file (if created 'added 'modified) (file-relative-name ignore-name))))
(git-run-command nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
(git-update-status-files (list (file-relative-name ignore-name)) 'unknown)))
; propertize definition for XEmacs, stolen from erc-compat
(eval-when-compile
@@ -523,23 +523,39 @@ and returns the process output as a string."
" " (git-escape-file-name (git-fileinfo->name info))
(git-rename-as-string info))))
(defun git-parse-status (status)
"Parse the output of git-diff-index in the current buffer."
(goto-char (point-min))
(while (re-search-forward
":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
nil t 1)
(let ((old-perm (string-to-number (match-string 1) 8))
(new-perm (string-to-number (match-string 2) 8))
(state (or (match-string 4) (match-string 6)))
(name (or (match-string 5) (match-string 7)))
(new-name (match-string 8)))
(if new-name ; copy or rename
(if (eq ?C (string-to-char state))
(ewoc-enter-last status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name))
(ewoc-enter-last status (git-create-fileinfo 'deleted name 0 0 'rename new-name))
(ewoc-enter-last status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)))
(ewoc-enter-last status (git-create-fileinfo (git-state-code state) name old-perm new-perm))))))
(defun git-insert-fileinfo (status info &optional refresh)
"Insert INFO in the status buffer, optionally refreshing an existing one."
(let ((node (and refresh
(git-find-status-file status (git-fileinfo->name info)))))
(setf (git-fileinfo->needs-refresh info) t)
(when node ;preserve the marked flag
(setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))))
(if node (ewoc-set-data node info) (ewoc-enter-last status info))))
(defun git-run-diff-index (status files)
"Run git-diff-index on FILES and parse the results into STATUS.
Return the list of files that haven't been handled."
(let ((refresh files))
(with-temp-buffer
(apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files)
(goto-char (point-min))
(while (re-search-forward
":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
nil t 1)
(let ((old-perm (string-to-number (match-string 1) 8))
(new-perm (string-to-number (match-string 2) 8))
(state (or (match-string 4) (match-string 6)))
(name (or (match-string 5) (match-string 7)))
(new-name (match-string 8)))
(if new-name ; copy or rename
(if (eq ?C (string-to-char state))
(git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) refresh)
(git-insert-fileinfo status (git-create-fileinfo 'deleted name 0 0 'rename new-name) refresh)
(git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)) refresh)
(git-insert-fileinfo status (git-create-fileinfo (git-state-code state) name old-perm new-perm) refresh))
(setq files (delete name files))
(when new-name (setq files (delete new-name files)))))))
files)
(defun git-find-status-file (status file)
"Find a given file in the status ewoc and return its node."
@@ -548,32 +564,59 @@ and returns the process output as a string."
(setq node (ewoc-next status node)))
node))
(defun git-parse-ls-files (status default-state &optional skip-existing)
"Parse the output of git-ls-files in the current buffer."
(goto-char (point-min))
(let (infolist)
(while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1)
(let ((state (match-string 1))
(name (match-string 2)))
(unless (and skip-existing (git-find-status-file status name))
(push (git-create-fileinfo (or (git-state-code state) default-state) name) infolist))))
(dolist (info (nreverse infolist))
(ewoc-enter-last status info))))
(defun git-run-ls-files (status files default-state &rest options)
"Run git-ls-files on FILES and parse the results into STATUS.
Return the list of files that haven't been handled."
(let ((refresh files))
(with-temp-buffer
(apply #'git-run-command t nil "ls-files" "-z" "-t" (append options (list "--") files))
(goto-char (point-min))
(while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1)
(let ((state (match-string 1))
(name (match-string 2)))
(git-insert-fileinfo status (git-create-fileinfo (or (git-state-code state) default-state) name) refresh)
(setq files (delete name files))))))
files)
(defun git-parse-ls-unmerged (status)
"Parse the output of git-ls-files -u in the current buffer."
(goto-char (point-min))
(let (files)
(while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
(let ((node (git-find-status-file status (match-string 1))))
(when node (push (ewoc-data node) files))))
(git-set-files-state files 'unmerged)))
(defun git-run-ls-unmerged (status files)
"Run git-ls-files -u on FILES and parse the results into STATUS."
(with-temp-buffer
(apply #'git-run-command t nil "ls-files" "-z" "-u" "--" files)
(goto-char (point-min))
(let (unmerged-files)
(while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
(let ((node (git-find-status-file status (match-string 1))))
(when node (push (ewoc-data node) unmerged-files))))
(git-set-files-state unmerged-files 'unmerged))))
(defun git-add-status-file (state name)
"Add a new file to the status list (if not existing already) and return its node."
(defun git-update-status-files (files &optional default-state)
"Update the status of FILES from the index."
(unless git-status (error "Not in git-status buffer."))
(or (git-find-status-file git-status name)
(ewoc-enter-last git-status (git-create-fileinfo state name))))
(let* ((status git-status)
(remaining-files
(if (git-empty-db-p) ; we need some special handling for an empty db
(git-run-ls-files status files 'added "-c")
(git-run-diff-index status files))))
(git-run-ls-unmerged status files)
(when (and (or (not files) remaining-files)
(file-readable-p ".git/info/exclude"))
(setq remaining-files (git-run-ls-files status remaining-files
'unknown "-o" "--exclude-from=.git/info/exclude"
(concat "--exclude-per-directory=" git-per-dir-ignore-file))))
; mark remaining files with the default state (or remove them if nil)
(when remaining-files
(if default-state
(ewoc-map (lambda (info)
(when (member (git-fileinfo->name info) remaining-files)
(git-set-files-state (list info) default-state))
nil)
status)
(ewoc-filter status
(lambda (info files)
(not (member (git-fileinfo->name info) files)))
remaining-files)))
(git-refresh-files)
(git-refresh-ewoc-hf status)))
(defun git-marked-files ()
"Return a list of all marked files, or if none a list containing just the file at cursor position."
@@ -789,54 +832,34 @@ and returns the process output as a string."
(defun git-add-file ()
"Add marked file(s) to the index cache."
(interactive)
(let ((files (git-marked-files-state 'unknown)))
(let ((files (git-get-filenames (git-marked-files-state 'unknown))))
(unless files
(push (ewoc-data
(git-add-status-file 'added (file-relative-name
(read-file-name "File to add: " nil nil t))))
files))
(apply #'git-run-command nil nil "update-index" "--info-only" "--add" "--" (git-get-filenames files))
(git-set-files-state files 'added)
(git-refresh-files)))
(push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
(apply #'git-run-command nil nil "update-index" "--add" "--" files)
(git-update-status-files files 'uptodate)))
(defun git-ignore-file ()
"Add marked file(s) to the ignore list."
(interactive)
(let ((files (git-marked-files-state 'unknown)))
(let ((files (git-get-filenames (git-marked-files-state 'unknown))))
(unless files
(push (ewoc-data
(git-add-status-file 'unknown (file-relative-name
(read-file-name "File to ignore: " nil nil t))))
files))
(dolist (info files) (git-append-to-ignore (git-fileinfo->name info)))
(git-set-files-state files 'ignored)
(git-refresh-files)))
(push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files))
(dolist (f files) (git-append-to-ignore f))
(git-update-status-files files 'ignored)))
(defun git-remove-file ()
"Remove the marked file(s)."
(interactive)
(let ((files (git-marked-files-state 'added 'modified 'unknown 'uptodate)))
(let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate))))
(unless files
(push (ewoc-data
(git-add-status-file 'unknown (file-relative-name
(read-file-name "File to remove: " nil nil t))))
files))
(push (file-relative-name (read-file-name "File to remove: " nil nil t)) files))
(if (yes-or-no-p
(format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" "")))
(progn
(dolist (info files)
(let ((name (git-fileinfo->name info)))
(when (file-exists-p name) (delete-file name))))
(apply #'git-run-command nil nil "update-index" "--info-only" "--remove" "--" (git-get-filenames files))
; remove unknown files from the list, set the others to deleted
(ewoc-filter git-status
(lambda (info files)
(not (and (memq info files) (eq (git-fileinfo->state info) 'unknown))))
files)
(git-set-files-state files 'deleted)
(git-refresh-files)
(unless (ewoc-nth git-status 0) ; refresh header if list is empty
(git-refresh-ewoc-hf git-status)))
(dolist (name files)
(when (file-exists-p name) (delete-file name)))
(apply #'git-run-command nil nil "update-index" "--remove" "--" files)
(git-update-status-files files nil))
(message "Aborting"))))
(defun git-revert-file ()
@@ -849,26 +872,23 @@ and returns the process output as a string."
(format "Revert %d file%s? " (length files) (if (> (length files) 1) "s" ""))))
(dolist (info files)
(case (git-fileinfo->state info)
('added (push info added))
('deleted (push info modified))
('unmerged (push info modified))
('modified (push info modified))))
('added (push (git-fileinfo->name info) added))
('deleted (push (git-fileinfo->name info) modified))
('unmerged (push (git-fileinfo->name info) modified))
('modified (push (git-fileinfo->name info) modified))))
(when added
(apply #'git-run-command nil nil "update-index" "--force-remove" "--" (git-get-filenames added))
(git-set-files-state added 'unknown))
(apply #'git-run-command nil nil "update-index" "--force-remove" "--" added))
(when modified
(apply #'git-run-command nil nil "checkout" "HEAD" (git-get-filenames modified))
(git-set-files-state modified 'uptodate))
(git-refresh-files))))
(apply #'git-run-command nil nil "checkout" "HEAD" modified))
(git-update-status-files (append added modified) 'uptodate))))
(defun git-resolve-file ()
"Resolve conflicts in marked file(s)."
(interactive)
(let ((files (git-marked-files-state 'unmerged)))
(let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
(when files
(apply #'git-run-command nil nil "update-index" "--" (git-get-filenames files))
(git-set-files-state files 'modified)
(git-refresh-files))))
(apply #'git-run-command nil nil "update-index" "--" files)
(git-update-status-files files 'uptodate))))
(defun git-remove-handled ()
"Remove handled files from the status list."
@@ -1038,7 +1058,7 @@ and returns the process output as a string."
(let ((info (ewoc-data (ewoc-locate git-status))))
(find-file (git-fileinfo->name info))
(when (eq 'unmerged (git-fileinfo->state info))
(smerge-mode))))
(smerge-mode 1))))
(defun git-find-file-other-window ()
"Visit the current file in its own buffer in another window."
@@ -1071,27 +1091,9 @@ and returns the process output as a string."
(pos (ewoc-locate status))
(cur-name (and pos (git-fileinfo->name (ewoc-data pos)))))
(unless status (error "Not in git-status buffer."))
(git-run-command nil nil "update-index" "--refresh")
(git-clear-status status)
(git-run-command nil nil "update-index" "--info-only" "--refresh")
(if (git-empty-db-p)
; we need some special handling for an empty db
(with-temp-buffer
(git-run-command t nil "ls-files" "-z" "-t" "-c")
(git-parse-ls-files status 'added))
(with-temp-buffer
(git-run-command t nil "diff-index" "-z" "-M" "HEAD")
(git-parse-status status)))
(with-temp-buffer
(git-run-command t nil "ls-files" "-z" "-u")
(git-parse-ls-unmerged status))
(when (file-readable-p ".git/info/exclude")
(with-temp-buffer
(git-run-command t nil "ls-files" "-z" "-t" "-o"
"--exclude-from=.git/info/exclude"
(concat "--exclude-per-directory=" git-per-dir-ignore-file))
(git-parse-ls-files status 'unknown)))
(git-refresh-files)
(git-refresh-ewoc-hf status)
(git-update-status-files nil)
; move point to the current file name if any
(let ((node (and cur-name (git-find-status-file status cur-name))))
(when node (ewoc-goto-node status node)))))

View File

@@ -63,21 +63,34 @@ def system(cmd):
if os.system(cmd) != 0:
die("command failed: %s" % cmd)
def p4CmdList(cmd):
def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
cmd = "p4 -G %s" % cmd
if verbose:
sys.stderr.write("Opening pipe: %s\n" % cmd)
pipe = os.popen(cmd, "rb")
# Use a temporary file to avoid deadlocks without
# subprocess.communicate(), which would put another copy
# of stdout into memory.
stdin_file = None
if stdin is not None:
stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode)
stdin_file.write(stdin)
stdin_file.flush()
stdin_file.seek(0)
p4 = subprocess.Popen(cmd, shell=True,
stdin=stdin_file,
stdout=subprocess.PIPE)
result = []
try:
while True:
entry = marshal.load(pipe)
entry = marshal.load(p4.stdout)
result.append(entry)
except EOFError:
pass
exitCode = pipe.close()
if exitCode != None:
exitCode = p4.wait()
if exitCode != 0:
entry = {}
entry["p4ExitCode"] = exitCode
result.append(entry)
@@ -168,27 +181,55 @@ def gitBranchExists(branch):
def gitConfig(key):
return read_pipe("git config %s" % key, ignore_error=True).strip()
def p4BranchesInGit(branchesAreInRemotes = True):
branches = {}
cmdline = "git rev-parse --symbolic "
if branchesAreInRemotes:
cmdline += " --remotes"
else:
cmdline += " --branches"
for line in read_pipe_lines(cmdline):
line = line.strip()
## only import to p4/
if not line.startswith('p4/') or line == "p4/HEAD":
continue
branch = line
# strip off p4
branch = re.sub ("^p4/", "", line)
branches[branch] = parseRevision(line)
return branches
def findUpstreamBranchPoint(head = "HEAD"):
branches = p4BranchesInGit()
# map from depot-path to branch name
branchByDepotPath = {}
for branch in branches.keys():
tip = branches[branch]
log = extractLogMessageFromGitCommit(tip)
settings = extractSettingsGitLog(log)
if settings.has_key("depot-paths"):
paths = ",".join(settings["depot-paths"])
branchByDepotPath[paths] = "remotes/p4/" + branch
settings = None
branchPoint = ""
parent = 0
while parent < 65535:
commit = head + "~%s" % parent
log = extractLogMessageFromGitCommit(commit)
settings = extractSettingsGitLog(log)
if not settings.has_key("depot-paths"):
parent = parent + 1
continue
if settings.has_key("depot-paths"):
paths = ",".join(settings["depot-paths"])
if branchByDepotPath.has_key(paths):
return [branchByDepotPath[paths], settings]
names = read_pipe_lines("git name-rev \"--refs=refs/remotes/p4/*\" \"%s\"" % commit)
if len(names) <= 0:
continue
parent = parent + 1
# strip away the beginning of 'HEAD~42 refs/remotes/p4/foo'
branchPoint = names[0].strip()[len(commit) + 1:]
break
return [branchPoint, settings]
return ["", settings]
class Command:
def __init__(self):
@@ -712,27 +753,13 @@ class P4Sync(Command):
if not files:
return
# We cannot put all the files on the command line
# OS have limitations on the max lenght of arguments
# POSIX says it's 4096 bytes, default for Linux seems to be 130 K.
# and all OS from the table below seems to be higher than POSIX.
# See http://www.in-ulm.de/~mascheck/various/argmax/
if (self.isWindows):
argmax = 2000
else:
argmax = min(4000, os.sysconf('SC_ARG_MAX'))
chunk = ''
filedata = []
for i in xrange(len(files)):
f = files[i]
chunk += '"%s#%s" ' % (f['path'], f['rev'])
if len(chunk) > argmax or i == len(files)-1:
data = p4CmdList('print %s' % chunk)
if "p4ExitCode" in data[0]:
die("Problems executing p4. Error: [%d]." % (data[0]['p4ExitCode']));
filedata.extend(data)
chunk = ''
filedata = p4CmdList('-x - print',
stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
for f in files]),
stdin_mode='w+')
if "p4ExitCode" in filedata[0]:
die("Problems executing p4. Error: [%d]."
% (filedata[0]['p4ExitCode']));
j = 0;
contents = {}
@@ -874,7 +901,8 @@ class P4Sync(Command):
% (labelDetails["label"], change))
def getUserCacheFilename(self):
return os.environ["HOME"] + "/.gitp4-usercache.txt"
home = os.environ.get("HOME", os.environ.get("USERPROFILE"))
return home + "/.gitp4-usercache.txt"
def getUserMapFromPerforceServer(self):
if self.userMapFromPerforceServer:
@@ -979,27 +1007,11 @@ class P4Sync(Command):
self.knownBranches[branch] = branch
def listExistingP4GitBranches(self):
self.p4BranchesInGit = []
cmdline = "git rev-parse --symbolic "
if self.importIntoRemotes:
cmdline += " --remotes"
else:
cmdline += " --branches"
for line in read_pipe_lines(cmdline):
line = line.strip()
## only import to p4/
if not line.startswith('p4/') or line == "p4/HEAD":
continue
branch = line
# strip off p4
branch = re.sub ("^p4/", "", line)
self.p4BranchesInGit.append(branch)
self.initialParents[self.refPrefix + branch] = parseRevision(line)
# branches holds mapping from name to commit
branches = p4BranchesInGit(self.importIntoRemotes)
self.p4BranchesInGit = branches.keys()
for branch in branches.keys():
self.initialParents[self.refPrefix + branch] = branches[branch]
def createOrUpdateBranchesFromOrigin(self):
if not self.silent:

9
date.c
View File

@@ -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
View File

@@ -1696,7 +1696,7 @@ static void prep_temp_blob(struct diff_tempfile *temp,
fd = git_mkstemp(temp->tmp_path, PATH_MAX, ".diff_XXXXXX");
if (fd < 0)
die("unable to create temp-file");
die("unable to create temp-file: %s", strerror(errno));
if (write_in_full(fd, blob, size) != size)
die("unable to write temp-file");
close(fd);

37
entry.c
View File

@@ -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);
}
}

View File

@@ -284,6 +284,12 @@ do
git mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
<"$dotest/$msgnum" >"$dotest/info" ||
stop_here $this
# skip pine's internal folder data
grep '^Author: Mail System Internal Data$' \
<"$dotest"/info >/dev/null &&
go_next && continue
test -s $dotest/patch || {
echo "Patch is empty. Was it split wrong?"
stop_here $this
@@ -364,7 +370,7 @@ do
[yY]*) action=yes ;;
[aA]*) action=yes interactive= ;;
[nN]*) action=skip ;;
[eE]*) "${VISUAL:-${EDITOR:-vi}}" "$dotest/final-commit"
[eE]*) git_editor "$dotest/final-commit"
action=again ;;
[vV]*) action=again
LESS=-S ${PAGER:-less} "$dotest/patch" ;;

View File

@@ -3,7 +3,7 @@
# Copyright (c) 2005 Linus Torvalds
# Copyright (c) 2006 Junio C Hamano
USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [[-i | -o] <path>...]'
USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]'
SUBDIRECTORY_OK=Yes
. git-sh-setup
require_work_tree
@@ -87,6 +87,7 @@ signoff=
force_author=
only_include_assumed=
untracked_files=
templatefile="`git config commit.template`"
while case "$#" in 0) break;; esac
do
case "$1" in
@@ -248,6 +249,13 @@ $1"
signoff=t
shift
;;
-t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template)
case "$#" in 1) usage ;; esac
shift
templatefile="$1"
no_edit=
shift
;;
-q|--q|--qu|--qui|--quie|--quiet)
quiet=t
shift
@@ -321,6 +329,14 @@ t,,[1-9]*)
die "No paths with -i does not make sense." ;;
esac
if test ! -z "$templatefile" -a -z "$log_given"
then
if test ! -f "$templatefile"
then
die "Commit template file does not exist."
fi
fi
################################################################
# Prepare index to have a tree to be committed
@@ -454,6 +470,9 @@ then
elif test -f "$GIT_DIR/SQUASH_MSG"
then
cat "$GIT_DIR/SQUASH_MSG"
elif test "$templatefile" != ""
then
cat "$templatefile"
fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG
case "$signoff" in
@@ -544,18 +563,9 @@ fi
case "$no_edit" in
'')
case "${VISUAL:-$EDITOR},$TERM" in
,dumb)
echo >&2 "Terminal is dumb but no VISUAL nor EDITOR defined."
echo >&2 "Please supply the commit log message using either"
echo >&2 "-m or -F option. A boilerplate log message has"
echo >&2 "been prepared in $GIT_DIR/COMMIT_EDITMSG"
exit 1
;;
esac
git-var GIT_AUTHOR_IDENT > /dev/null || die
git-var GIT_COMMITTER_IDENT > /dev/null || die
${VISUAL:-${EDITOR:-vi}} "$GIT_DIR/COMMIT_EDITMSG"
git_editor "$GIT_DIR/COMMIT_EDITMSG"
;;
esac
@@ -581,10 +591,35 @@ else
fi |
git stripspace >"$GIT_DIR"/COMMIT_MSG
if cnt=`grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
git stripspace |
wc -l` &&
test 0 -lt $cnt
# Test whether the commit message has any content we didn't supply.
have_commitmsg=
grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
git stripspace > "$GIT_DIR"/COMMIT_BAREMSG
# Is the commit message totally empty?
if test -s "$GIT_DIR"/COMMIT_BAREMSG
then
if test "$templatefile" != ""
then
# Test whether this is just the unaltered template.
if cnt=`sed -e '/^#/d' < "$templatefile" |
git stripspace |
diff "$GIT_DIR"/COMMIT_BAREMSG - |
wc -l` &&
test 0 -lt $cnt
then
have_commitmsg=t
fi
else
# No template, so the content in the commit message must
# have come from the user.
have_commitmsg=t
fi
fi
rm -f "$GIT_DIR"/COMMIT_BAREMSG
if test "$have_commitmsg" = "t"
then
if test -z "$TMP_INDEX"
then

View File

@@ -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

View File

@@ -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

View File

@@ -154,12 +154,10 @@ proc gitexec {args} {
}
proc reponame {} {
global _reponame
return $_reponame
return $::_reponame
}
proc is_MacOSX {} {
global tcl_platform tk_library
if {[tk windowingsystem] eq {aqua}} {
return 1
}
@@ -167,17 +165,16 @@ proc is_MacOSX {} {
}
proc is_Windows {} {
global tcl_platform
if {$tcl_platform(platform) eq {windows}} {
if {$::tcl_platform(platform) eq {windows}} {
return 1
}
return 0
}
proc is_Cygwin {} {
global tcl_platform _iscygwin
global _iscygwin
if {$_iscygwin eq {}} {
if {$tcl_platform(platform) eq {windows}} {
if {$::tcl_platform(platform) eq {windows}} {
if {[catch {set p [exec cygpath --windir]} err]} {
set _iscygwin 0
} else {
@@ -367,16 +364,25 @@ proc _which {what} {
return {}
}
proc _lappend_nice {cmd_var} {
global _nice
upvar $cmd_var cmd
if {![info exists _nice]} {
set _nice [_which nice]
}
if {$_nice ne {}} {
lappend cmd $_nice
}
}
proc git {args} {
set opt [list exec]
while {1} {
switch -- [lindex $args 0] {
--nice {
global _nice
if {$_nice ne {}} {
lappend opt $_nice
}
_lappend_nice opt
}
default {
@@ -414,6 +420,7 @@ proc _open_stdout_stderr {cmd} {
error $err
}
}
fconfigure $fd -eofchar {}
return $fd
}
@@ -423,10 +430,7 @@ proc git_read {args} {
while {1} {
switch -- [lindex $args 0] {
--nice {
global _nice
if {$_nice ne {}} {
lappend opt $_nice
}
_lappend_nice opt
}
--stderr {
@@ -454,10 +458,7 @@ proc git_write {args} {
while {1} {
switch -- [lindex $args 0] {
--nice {
global _nice
if {$_nice ne {}} {
lappend opt $_nice
}
_lappend_nice opt
}
default {
@@ -524,7 +525,6 @@ if {$_git eq {}} {
error_popup "Cannot find git in PATH."
exit 1
}
set _nice [_which nice]
######################################################################
##
@@ -544,8 +544,34 @@ if {![regsub {^git version } $_git_version {} _git_version]} {
error_popup "Cannot parse Git version string:\n\n$_git_version"
exit 1
}
set _real_git_version $_git_version
regsub -- {-dirty$} $_git_version {} _git_version
regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
regsub {\.rc[0-9]+$} $_git_version {} _git_version
regsub {\.GIT$} $_git_version {} _git_version
if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
catch {wm withdraw .}
if {[tk_messageBox \
-icon warning \
-type yesno \
-default no \
-title "[appname]: warning" \
-message "Git version cannot be determined.
$_git claims it is version '$_real_git_version'.
[appname] requires at least Git 1.5.0 or later.
Assume '$_real_git_version' is version 1.5.0?
"] eq {yes}} {
set _git_version 1.5.0
} else {
exit 1
}
}
unset _real_git_version
proc git-version {args} {
global _git_version
@@ -601,6 +627,46 @@ You are using [git-version]:
exit 1
}
######################################################################
##
## feature option selection
if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
unset _junk
} else {
set subcommand gui
}
if {$subcommand eq {gui.sh}} {
set subcommand gui
}
if {$subcommand eq {gui} && [llength $argv] > 0} {
set subcommand [lindex $argv 0]
set argv [lrange $argv 1 end]
}
enable_option multicommit
enable_option branch
enable_option transport
disable_option bare
switch -- $subcommand {
browser -
blame {
enable_option bare
disable_option multicommit
disable_option branch
disable_option transport
}
citool {
enable_option singlecommit
disable_option multicommit
disable_option branch
disable_option transport
}
}
######################################################################
##
## repository setup
@@ -625,19 +691,24 @@ if {![file isdirectory $_gitdir]} {
error_popup "Git directory not found:\n\n$_gitdir"
exit 1
}
if {[lindex [file split $_gitdir] end] ne {.git}} {
catch {wm withdraw .}
error_popup "Cannot use funny .git directory:\n\n$_gitdir"
exit 1
if {![is_enabled bare]} {
if {[lindex [file split $_gitdir] end] ne {.git}} {
catch {wm withdraw .}
error_popup "Cannot use funny .git directory:\n\n$_gitdir"
exit 1
}
if {[catch {cd [file dirname $_gitdir]} err]} {
catch {wm withdraw .}
error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
exit 1
}
}
if {[catch {cd [file dirname $_gitdir]} err]} {
catch {wm withdraw .}
error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
exit 1
set _reponame [file split [file normalize $_gitdir]]
if {[lindex $_reponame end] eq {.git}} {
set _reponame [lindex $_reponame end-1]
} else {
set _reponame [lindex $_reponame end]
}
set _reponame [lindex [file split \
[file normalize [file dirname $_gitdir]]] \
end]
######################################################################
##
@@ -758,8 +829,9 @@ proc rescan {after {honor_trustmtime 1}} {
array unset file_states
if {![$ui_comm edit modified]
|| [string trim [$ui_comm get 0.0 end]] eq {}} {
if {!$::GITGUI_BCK_exists &&
(![$ui_comm edit modified]
|| [string trim [$ui_comm get 0.0 end]] eq {})} {
if {[string match amend* $commit_type]} {
} elseif {[load_message GITGUI_MSG]} {
} elseif {[load_message MERGE_MSG]} {
@@ -800,6 +872,10 @@ proc rescan_stage2 {fd after} {
if {[file readable $info_exclude]} {
lappend ls_others "--exclude-from=$info_exclude"
}
set user_exclude [get_config core.excludesfile]
if {$user_exclude ne {} && [file readable $user_exclude]} {
lappend ls_others "--exclude-from=$user_exclude"
}
set buf_rdi {}
set buf_rdf {}
@@ -827,6 +903,7 @@ proc load_message {file} {
if {[catch {set fd [open $f r]}]} {
return 0
}
fconfigure $fd -eofchar {}
set content [string trim [read $fd]]
close $fd
regsub -all -line {[ \r\t]+$} $content {} content
@@ -1219,32 +1296,6 @@ static unsigned char file_merge_bits[] = {
0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
} -maskdata $filemask
set file_dir_data {
#define file_width 18
#define file_height 18
static unsigned char file_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
}
image create bitmap file_dir -background white -foreground blue \
-data $file_dir_data -maskdata $file_dir_data
unset file_dir_data
set file_uplevel_data {
#define up_width 15
#define up_height 15
static unsigned char up_bits[] = {
0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
}
image create bitmap file_uplevel -background white -foreground red \
-data $file_uplevel_data -maskdata $file_uplevel_data
unset file_uplevel_data
set ui_index .vpane.files.index.list
set ui_workdir .vpane.files.workdir.list
@@ -1345,6 +1396,7 @@ set is_quitting 0
proc do_quit {} {
global ui_comm is_quitting repo_config commit_type
global GITGUI_BCK_exists GITGUI_BCK_i
if {$is_quitting} return
set is_quitting 1
@@ -1353,18 +1405,30 @@ proc do_quit {} {
# -- Stash our current commit buffer.
#
set save [gitdir GITGUI_MSG]
set msg [string trim [$ui_comm get 0.0 end]]
regsub -all -line {[ \r\t]+$} $msg {} msg
if {(![string match amend* $commit_type]
|| [$ui_comm edit modified])
&& $msg ne {}} {
catch {
set fd [open $save w]
puts -nonewline $fd $msg
close $fd
}
if {$GITGUI_BCK_exists && ![$ui_comm edit modified]} {
file rename -force [gitdir GITGUI_BCK] $save
set GITGUI_BCK_exists 0
} else {
catch {file delete $save}
set msg [string trim [$ui_comm get 0.0 end]]
regsub -all -line {[ \r\t]+$} $msg {} msg
if {(![string match amend* $commit_type]
|| [$ui_comm edit modified])
&& $msg ne {}} {
catch {
set fd [open $save w]
puts -nonewline $fd $msg
close $fd
}
} else {
catch {file delete $save}
}
}
# -- Remove our editor backup, its not needed.
#
after cancel $GITGUI_BCK_i
if {$GITGUI_BCK_exists} {
catch {file delete [gitdir GITGUI_BCK]}
}
# -- Stash our current window geometry into this repository.
@@ -1566,43 +1630,6 @@ set font_descs {
load_config 0
apply_config
######################################################################
##
## feature option selection
if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
unset _junk
} else {
set subcommand gui
}
if {$subcommand eq {gui.sh}} {
set subcommand gui
}
if {$subcommand eq {gui} && [llength $argv] > 0} {
set subcommand [lindex $argv 0]
set argv [lrange $argv 1 end]
}
enable_option multicommit
enable_option branch
enable_option transport
switch -- $subcommand {
browser -
blame {
disable_option multicommit
disable_option branch
disable_option transport
}
citool {
enable_option singlecommit
disable_option multicommit
disable_option branch
disable_option transport
}
}
######################################################################
##
## ui construction
@@ -1632,20 +1659,32 @@ if {[is_enabled transport]} {
menu .mbar.repository
.mbar.repository add command \
-label {Browse Current Branch} \
-label {Browse Current Branch's Files} \
-command {browser::new $current_branch}
trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
set ui_browse_current [.mbar.repository index last]
.mbar.repository add command \
-label {Browse Branch Files...} \
-command browser_open::dialog
.mbar.repository add separator
.mbar.repository add command \
-label {Visualize Current Branch} \
-label {Visualize Current Branch's History} \
-command {do_gitk $current_branch}
trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
set ui_visualize_current [.mbar.repository index last]
.mbar.repository add command \
-label {Visualize All Branches} \
-label {Visualize All Branch History} \
-command {do_gitk --all}
.mbar.repository add separator
proc current_branch_write {args} {
global current_branch
.mbar.repository entryconf $::ui_browse_current \
-label "Browse $current_branch's Files"
.mbar.repository entryconf $::ui_visualize_current \
-label "Visualize $current_branch's History"
}
trace add variable current_branch write current_branch_write
if {[is_enabled multicommit]} {
.mbar.repository add command -label {Database Statistics} \
-command do_stats
@@ -1766,12 +1805,12 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
lappend disable_on_lock \
[list .mbar.commit entryconf [.mbar.commit index last] -state]
.mbar.commit add command -label {Add To Commit} \
.mbar.commit add command -label {Stage To Commit} \
-command do_add_selection
lappend disable_on_lock \
[list .mbar.commit entryconf [.mbar.commit index last] -state]
.mbar.commit add command -label {Add Existing To Commit} \
.mbar.commit add command -label {Stage Changed Files To Commit} \
-command do_add_all \
-accelerator $M1T-I
lappend disable_on_lock \
@@ -1805,14 +1844,14 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
if {[is_enabled branch]} {
menu .mbar.merge
.mbar.merge add command -label {Local Merge...} \
-command merge::dialog
-command merge::dialog \
-accelerator $M1T-M
lappend disable_on_lock \
[list .mbar.merge entryconf [.mbar.merge index last] -state]
.mbar.merge add command -label {Abort Merge...} \
-command merge::reset_hard
lappend disable_on_lock \
[list .mbar.merge entryconf [.mbar.merge index last] -state]
}
# -- Transport Menu
@@ -1844,33 +1883,6 @@ if {[is_MacOSX]} {
.mbar.edit add separator
.mbar.edit add command -label {Options...} \
-command do_options
# -- Tools Menu
#
if {[is_Cygwin] && [file exists /usr/local/miga/lib/gui-miga]} {
proc do_miga {} {
if {![lock_index update]} return
set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""]
set miga_fd [open "|$cmd" r]
fconfigure $miga_fd -blocking 0
fileevent $miga_fd readable [list miga_done $miga_fd]
ui_status {Running miga...}
}
proc miga_done {fd} {
read $fd 512
if {[eof $fd]} {
close $fd
unlock_index
rescan ui_ready
}
}
.mbar add cascade -label Tools -menu .mbar.tools
menu .mbar.tools
.mbar.tools add command -label "Migrate" \
-command do_miga
lappend disable_on_lock \
[list .mbar.tools entryconf [.mbar.tools index last] -state]
}
}
# -- Help Menu
@@ -1938,29 +1950,10 @@ proc usage {} {
# -- Not a normal commit type invocation? Do that instead!
#
switch -- $subcommand {
browser {
set subcommand_args {rev?}
switch [llength $argv] {
0 { load_current_branch }
1 {
set current_branch [lindex $argv 0]
if {[regexp {^[0-9a-f]{1,39}$} $current_branch]} {
if {[catch {
set current_branch \
[git rev-parse --verify $current_branch]
} err]} {
puts stderr $err
exit 1
}
}
}
default usage
}
browser::new $current_branch
return
}
browser -
blame {
set subcommand_args {rev? path?}
set subcommand_args {rev? path}
if {$argv eq {}} usage
set head {}
set path {}
set is_path 0
@@ -1979,12 +1972,18 @@ blame {
} elseif {$head eq {}} {
if {$head ne {}} usage
set head $a
set is_path 1
} else {
usage
}
}
unset is_path
if {$head ne {} && $path eq {}} {
set path $_prefix$head
set head {}
}
if {$head eq {}} {
load_current_branch
} else {
@@ -1999,8 +1998,26 @@ blame {
set current_branch $head
}
if {$path eq {}} usage
blame::new $head $path
switch -- $subcommand {
browser {
if {$head eq {}} {
if {$path ne {} && [file isdirectory $path]} {
set head $current_branch
} else {
set head $path
set path {}
}
}
browser::new $head $path
}
blame {
if {$head eq {} && ![file exists $path]} {
puts stderr "fatal: cannot stat path $path: No such file or directory"
exit 1
}
blame::new $head $path
}
}
return
}
citool -
@@ -2115,7 +2132,7 @@ pack .vpane.lower.commarea.buttons.rescan -side top -fill x
lappend disable_on_lock \
{.vpane.lower.commarea.buttons.rescan conf -state}
button .vpane.lower.commarea.buttons.incall -text {Add Existing} \
button .vpane.lower.commarea.buttons.incall -text {Stage Changed} \
-command do_add_all
pack .vpane.lower.commarea.buttons.incall -side top -fill x
lappend disable_on_lock \
@@ -2389,17 +2406,25 @@ lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
$ctxm add separator
$ctxm add command -label {Options...} \
-command do_options
bind_button3 $ui_diff "
set cursorX %x
set cursorY %y
if {\$ui_index eq \$current_diff_side} {
$ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit}
proc popup_diff_menu {ctxm x y X Y} {
set ::cursorX $x
set ::cursorY $y
if {$::ui_index eq $::current_diff_side} {
$ctxm entryconf $::ui_diff_applyhunk \
-state normal \
-label {Unstage Hunk From Commit}
} elseif {{_O} eq [lindex $::file_states($::current_diff_path) 0]} {
$ctxm entryconf $::ui_diff_applyhunk \
-state disabled \
-label {Stage Hunk For Commit}
} else {
$ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit}
$ctxm entryconf $::ui_diff_applyhunk \
-state normal \
-label {Stage Hunk For Commit}
}
tk_popup $ctxm %X %Y
"
unset ui_diff_applyhunk
tk_popup $ctxm $X $Y
}
bind_button3 $ui_diff [list popup_diff_menu $ctxm %x %y %X %Y]
# -- Status Bar
#
@@ -2460,6 +2485,8 @@ if {[is_enabled branch]} {
bind . <$M1B-Key-N> branch_create::dialog
bind . <$M1B-Key-o> branch_checkout::dialog
bind . <$M1B-Key-O> branch_checkout::dialog
bind . <$M1B-Key-m> merge::dialog
bind . <$M1B-Key-M> merge::dialog
}
if {[is_enabled transport]} {
bind . <$M1B-Key-p> do_push_anywhere
@@ -2551,24 +2578,64 @@ if {[is_enabled transport]} {
populate_push_menu
}
# -- Only suggest a gc run if we are going to stay running.
#
if {[is_enabled multicommit]} {
set object_limit 2000
if {[is_Windows]} {set object_limit 200}
regexp {^([0-9]+) objects,} [git count-objects] _junk objects_current
if {$objects_current >= $object_limit} {
if {[ask_popup \
"This repository currently has $objects_current loose objects.
if {[winfo exists $ui_comm]} {
set GITGUI_BCK_exists [load_message GITGUI_BCK]
To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
Compress the database now?"] eq yes} {
do_gc
# -- If both our backup and message files exist use the
# newer of the two files to initialize the buffer.
#
if {$GITGUI_BCK_exists} {
set m [gitdir GITGUI_MSG]
if {[file isfile $m]} {
if {[file mtime [gitdir GITGUI_BCK]] > [file mtime $m]} {
catch {file delete [gitdir GITGUI_MSG]}
} else {
$ui_comm delete 0.0 end
$ui_comm edit reset
$ui_comm edit modified false
catch {file delete [gitdir GITGUI_BCK]}
set GITGUI_BCK_exists 0
}
}
unset m
}
unset object_limit _junk objects_current
proc backup_commit_buffer {} {
global ui_comm GITGUI_BCK_exists
set m [$ui_comm edit modified]
if {$m || $GITGUI_BCK_exists} {
set msg [string trim [$ui_comm get 0.0 end]]
regsub -all -line {[ \r\t]+$} $msg {} msg
if {$msg eq {}} {
if {$GITGUI_BCK_exists} {
catch {file delete [gitdir GITGUI_BCK]}
set GITGUI_BCK_exists 0
}
} elseif {$m} {
catch {
set fd [open [gitdir GITGUI_BCK] w]
puts -nonewline $fd $msg
close $fd
set GITGUI_BCK_exists 1
}
}
$ui_comm edit modified false
}
set ::GITGUI_BCK_i [after 2000 backup_commit_buffer]
}
backup_commit_buffer
}
lock_index begin-read
if {![winfo ismapped .]} {
wm deiconify .
}
after 1 do_rescan
if {[is_enabled multicommit]} {
after 1000 hint_gc
}

View File

@@ -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]

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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 {}
}
}
}

View File

@@ -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

View File

@@ -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
}
}
}

View File

@@ -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
View 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 {}
}

View File

@@ -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} {

View File

@@ -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} \

View File

@@ -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
}
}

View File

@@ -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 {}

View File

@@ -17,8 +17,10 @@ USAGE='(--continue | --abort | --skip | [--preserve-merges] [--verbose]
require_work_tree
DOTEST="$GIT_DIR/.dotest-merge"
TODO="$DOTEST"/todo
TODO="$DOTEST"/git-rebase-todo
DONE="$DOTEST"/done
MSG="$DOTEST"/message
SQUASH_MSG="$DOTEST"/message-squash
REWRITTEN="$DOTEST"/rewritten
PRESERVE_MERGES=
STRATEGY=
@@ -31,6 +33,20 @@ warn () {
echo "$*" >&2
}
output () {
case "$VERBOSE" in
'')
"$@" > "$DOTEST"/output 2>&1
status=$?
test $status != 0 &&
cat "$DOTEST"/output
return $status
;;
*)
"$@"
esac
}
require_clean_work_tree () {
# test if working tree is dirty
git rev-parse --verify HEAD > /dev/null &&
@@ -54,6 +70,10 @@ mark_action_done () {
sed -e 1q < "$TODO" >> "$DONE"
sed -e 1d < "$TODO" >> "$TODO".new
mv -f "$TODO".new "$TODO"
count=$(($(wc -l < "$DONE")))
total=$(($count+$(wc -l < "$TODO")))
printf "Rebasing (%d/%d)\r" $count $total
test -z "$VERBOSE" || echo
}
make_patch () {
@@ -77,18 +97,18 @@ die_abort () {
pick_one () {
case "$1" in -n) sha1=$2 ;; *) sha1=$1 ;; esac
git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
test -d "$REWRITTEN" &&
pick_one_preserving_merges "$@" && return
parent_sha1=$(git rev-parse --verify $sha1^ 2>/dev/null)
current_sha1=$(git rev-parse --verify HEAD)
if [ $current_sha1 = $parent_sha1 ]; then
git reset --hard $sha1
test "a$1" = a-n && git reset --soft $current_sha1
if test $current_sha1 = $parent_sha1; then
output git reset --hard $sha1
test "a$1" = a-n && output git reset --soft $current_sha1
sha1=$(git rev-parse --short $sha1)
warn Fast forward to $sha1
output warn Fast forward to $sha1
else
git cherry-pick $STRATEGY "$@"
output git cherry-pick $STRATEGY "$@"
fi
}
@@ -96,7 +116,7 @@ pick_one_preserving_merges () {
case "$1" in -n) sha1=$2 ;; *) sha1=$1 ;; esac
sha1=$(git rev-parse $sha1)
if [ -f "$DOTEST"/current-commit ]
if test -f "$DOTEST"/current-commit
then
current_commit=$(cat "$DOTEST"/current-commit) &&
git rev-parse HEAD > "$REWRITTEN"/$current_commit &&
@@ -110,7 +130,7 @@ pick_one_preserving_merges () {
new_parents=
for p in $(git rev-list --parents -1 $sha1 | cut -d\ -f2-)
do
if [ -f "$REWRITTEN"/$p ]
if test -f "$REWRITTEN"/$p
then
preserve=f
new_p=$(cat "$REWRITTEN"/$p)
@@ -125,7 +145,7 @@ pick_one_preserving_merges () {
done
case $fast_forward in
t)
echo "Fast forward to $sha1"
output warn "Fast forward to $sha1"
test $preserve=f && echo $sha1 > "$REWRITTEN"/$sha1
;;
f)
@@ -133,7 +153,7 @@ pick_one_preserving_merges () {
first_parent=$(expr "$new_parents" : " \([^ ]*\)")
# detach HEAD to current parent
git checkout $first_parent 2> /dev/null ||
output git checkout $first_parent 2> /dev/null ||
die "Cannot move HEAD to $first_parent"
echo $sha1 > "$DOTEST"/current-commit
@@ -145,19 +165,51 @@ pick_one_preserving_merges () {
msg="$(git cat-file commit $sha1 | \
sed -e '1,/^$/d' -e "s/[\"\\]/\\\\&/g")"
# NEEDSWORK: give rerere a chance
if ! git merge $STRATEGY -m "$msg" $new_parents
if ! output git merge $STRATEGY -m "$msg" $new_parents
then
echo "$msg" > "$GIT_DIR"/MERGE_MSG
die Error redoing merge $sha1
fi
;;
*)
git cherry-pick $STRATEGY "$@" ||
output git cherry-pick $STRATEGY "$@" ||
die_with_patch $sha1 "Could not pick $sha1"
esac
esac
}
nth_string () {
case "$1" in
*1[0-9]|*[04-9]) echo "$1"th;;
*1) echo "$1"st;;
*2) echo "$1"nd;;
*3) echo "$1"rd;;
esac
}
make_squash_message () {
if test -f "$SQUASH_MSG"; then
COUNT=$(($(sed -n "s/^# This is [^0-9]*\([0-9]\+\).*/\1/p" \
< "$SQUASH_MSG" | tail -n 1)+1))
echo "# This is a combination of $COUNT commits."
sed -n "2,\$p" < "$SQUASH_MSG"
else
COUNT=2
echo "# This is a combination of two commits."
echo "# The first commit's message is:"
echo
git cat-file commit HEAD | sed -e '1,/^$/d'
echo
fi
echo "# This is the $(nth_string $COUNT) commit message:"
echo
git cat-file commit $1 | sed -e '1,/^$/d'
}
peek_next_command () {
sed -n "1s/ .*$//p" < "$TODO"
}
do_next () {
test -f "$DOTEST"/message && rm "$DOTEST"/message
test -f "$DOTEST"/author-script && rm "$DOTEST"/author-script
@@ -194,18 +246,22 @@ do_next () {
die "Cannot 'squash' without a previous commit"
mark_action_done
MSG="$DOTEST"/message
echo "# This is a combination of two commits." > "$MSG"
echo "# The first commit's message is:" >> "$MSG"
echo >> "$MSG"
git cat-file commit HEAD | sed -e '1,/^$/d' >> "$MSG"
echo >> "$MSG"
make_squash_message $sha1 > "$MSG"
case "$(peek_next_command)" in
squash)
EDIT_COMMIT=
USE_OUTPUT=output
cp "$MSG" "$SQUASH_MSG"
;;
*)
EDIT_COMMIT=-e
USE_OUTPUT=
test -f "$SQUASH_MSG" && rm "$SQUASH_MSG"
esac
failed=f
output git reset --soft HEAD^
pick_one -n $sha1 || failed=t
echo "# And this is the 2nd commit message:" >> "$MSG"
echo >> "$MSG"
git cat-file commit $sha1 | sed -e '1,/^$/d' >> "$MSG"
git reset --soft HEAD^
author_script=$(get_author_ident_from_commit $sha1)
echo "$author_script" > "$DOTEST"/author-script
case $failed in
@@ -213,7 +269,7 @@ do_next () {
# This is like --amend, but with a different message
eval "$author_script"
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
git commit -F "$MSG" -e
$USE_OUTPUT git commit -F "$MSG" $EDIT_COMMIT
;;
t)
cp "$MSG" "$GIT_DIR"/MERGE_MSG
@@ -232,7 +288,7 @@ do_next () {
HEADNAME=$(cat "$DOTEST"/head-name) &&
OLDHEAD=$(cat "$DOTEST"/head) &&
SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) &&
if [ -d "$REWRITTEN" ]
if test -d "$REWRITTEN"
then
test -f "$DOTEST"/current-commit &&
current_commit=$(cat "$DOTEST"/current-commit) &&
@@ -288,7 +344,7 @@ do
HEADNAME=$(cat "$DOTEST"/head-name)
HEAD=$(cat "$DOTEST"/head)
git symbolic-ref HEAD $HEADNAME &&
git reset --hard $HEAD &&
output git reset --hard $HEAD &&
rm -rf "$DOTEST"
exit
;;
@@ -297,7 +353,7 @@ do
test -d "$DOTEST" || die "No interactive rebase running"
git reset --hard && do_rest
output git reset --hard && do_rest
;;
-s|--strategy)
shift
@@ -349,11 +405,11 @@ do
require_clean_work_tree
if [ ! -z "$2"]
if test ! -z "$2"
then
git show-ref --verify --quiet "refs/heads/$2" ||
output git show-ref --verify --quiet "refs/heads/$2" ||
die "Invalid branchname: $2"
git checkout "$2" ||
output git checkout "$2" ||
die "Could not checkout $2"
fi
@@ -372,7 +428,7 @@ do
echo $ONTO > "$DOTEST"/onto
test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy
test t = "$VERBOSE" && : > "$DOTEST"/verbose
if [ t = "$PRESERVE_MERGES" ]
if test t = "$PRESERVE_MERGES"
then
# $REWRITTEN contains files for each commit that is
# reachable by at least one merge base of $HEAD and
@@ -414,13 +470,13 @@ EOF
die_abort "Nothing to do"
cp "$TODO" "$TODO".backup
${VISUAL:-${EDITOR:-vi}} "$TODO" ||
git_editor "$TODO" ||
die "Could not execute editor"
test -z "$(grep -ve '^$' -e '^#' < $TODO)" &&
die_abort "Nothing to do"
git checkout $ONTO && do_rest
output git checkout $ONTO && do_rest
esac
shift
done

View File

@@ -49,8 +49,8 @@ Options:
--bcc Specify a list of email addresses that should be Bcc:
on all the emails.
--compose Use \$EDITOR to edit an introductory message for the
patch series.
--compose Use \$GIT_EDITOR, core.editor, \$EDITOR, or \$VISUAL to edit
an introductory message for the patch series.
--subject Specify the initial "Subject:" line.
Only necessary if --compose is also set. If --compose
@@ -237,7 +237,7 @@ my %parse_alias = (
$aliases{$1} = [ split(/\s+/, $2) ];
}}},
pine => sub { my $fh = shift; while (<$fh>) {
if (/^(\S+)\s+(.*)$/) {
if (/^(\S+)\t.*\t(.*)$/) {
$aliases{$1} = [ split(/\s*,\s*/, $2) ];
}}},
gnus => sub { my $fh = shift; while (<$fh>) {
@@ -341,8 +341,7 @@ GIT: for the patch you are writing.
EOT
close(C);
my $editor = $ENV{EDITOR};
$editor = 'vi' unless defined $editor;
my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
system($editor, $compose_filename);
open(C2,">",$compose_filename . ".final")

View File

@@ -28,6 +28,21 @@ set_reflog_action() {
fi
}
git_editor() {
GIT_EDITOR=${GIT_EDITOR:-$(git config core.editor || echo ${VISUAL:-${EDITOR}})}
case "$GIT_EDITOR,$TERM" in
,dumb)
echo >&2 "No editor specified in GIT_EDITOR, core.editor, VISUAL,"
echo >&2 "or EDITOR. Tried to fall back to vi but terminal is dumb."
echo >&2 "Please set one of these variables to an appropriate"
echo >&2 "editor or run $0 with options that will not cause an"
echo >&2 "editor to be invoked (e.g., -m or -F for git-commit)."
exit 1
;;
esac
"${GIT_EDITOR:-vi}" "$1"
}
is_bare_repository () {
git rev-parse --is-bare-repository
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -740,7 +740,7 @@ sub load_authors {
my $log = $cmd eq 'log';
while (<$authors>) {
chomp;
next unless /^(\S+?|\(no author\))\s*=\s*(.+?)\s*<(.+)>\s*$/;
next unless /^(.+?|\(no author\))\s*=\s*(.+?)\s*<(.+)>\s*$/;
my ($user, $name, $email) = ($1, $2, $3);
if ($log) {
$Git::SVN::Log::rusers{"$name <$email>"} = $user;
@@ -2724,6 +2724,9 @@ sub repo_path {
sub url_path {
my ($self, $path) = @_;
if ($self->{url} =~ m#^https?://#) {
$path =~ s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
}
$self->{url} . '/' . $self->repo_path($path);
}

View File

@@ -177,7 +177,7 @@ if [ "$annotate" ]; then
( echo "#"
echo "# Write a tag message"
echo "#" ) > "$GIT_DIR"/TAG_EDITMSG
${VISUAL:-${EDITOR:-vi}} "$GIT_DIR"/TAG_EDITMSG || exit
git_editor "$GIT_DIR"/TAG_EDITMSG || exit
else
printf '%s\n' "$message" >"$GIT_DIR"/TAG_EDITMSG
fi

364
gitk
View File

@@ -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

View File

@@ -104,6 +104,59 @@ our $mimetypes_file = undef;
# could be even 'utf-8' for the old behavior)
our $fallback_encoding = 'latin1';
# rename detection options for git-diff and git-diff-tree
# - default is '-M', with the cost proportional to
# (number of removed files) * (number of new files).
# - more costly is '-C' (which implies '-M'), with the cost proportional to
# (number of changed files + number of removed files) * (number of new files)
# - even more costly is '-C', '--find-copies-harder' with cost
# (number of files in the original tree) * (number of new files)
# - one might want to include '-B' option, e.g. '-B', '-M'
our @diff_opts = ('-M'); # taken from git_commit
# information about snapshot formats that gitweb is capable of serving
our %known_snapshot_formats = (
# name => {
# 'display' => display name,
# 'type' => mime type,
# 'suffix' => filename suffix,
# 'format' => --format for git-archive,
# 'compressor' => [compressor command and arguments]
# (array reference, optional)}
#
'tgz' => {
'display' => 'tar.gz',
'type' => 'application/x-gzip',
'suffix' => '.tar.gz',
'format' => 'tar',
'compressor' => ['gzip']},
'tbz2' => {
'display' => 'tar.bz2',
'type' => 'application/x-bzip2',
'suffix' => '.tar.bz2',
'format' => 'tar',
'compressor' => ['bzip2']},
'zip' => {
'display' => 'zip',
'type' => 'application/x-zip',
'suffix' => '.zip',
'format' => 'zip'},
);
# Aliases so we understand old gitweb.snapshot values in repository
# configuration.
our %known_snapshot_format_aliases = (
'gzip' => 'tgz',
'bzip2' => 'tbz2',
# backward compatibility: legacy gitweb config support
'x-gzip' => undef, 'gz' => undef,
'x-bzip2' => undef, 'bz2' => undef,
'x-zip' => undef, '' => undef,
);
# You define site-wide feature defaults here; override them with
# $GITWEB_CONFIG as necessary.
our %feature = (
@@ -134,20 +187,22 @@ our %feature = (
'override' => 0,
'default' => [0]},
# Enable the 'snapshot' link, providing a compressed tarball of any
# Enable the 'snapshot' link, providing a compressed archive of any
# tree. This can potentially generate high traffic if you have large
# project.
# Value is a list of formats defined in %known_snapshot_formats that
# you wish to offer.
# To disable system wide have in $GITWEB_CONFIG
# $feature{'snapshot'}{'default'} = [undef];
# $feature{'snapshot'}{'default'} = [];
# To have project specific config enable override in $GITWEB_CONFIG
# $feature{'snapshot'}{'override'} = 1;
# and in project config gitweb.snapshot = none|gzip|bzip2|zip;
# and in project config, a comma-separated list of formats or "none"
# to disable. Example: gitweb.snapshot = tbz2,zip;
'snapshot' => {
'sub' => \&feature_snapshot,
'override' => 0,
# => [content-encoding, suffix, program]
'default' => ['x-gzip', 'gz', 'gzip']},
'default' => ['tgz']},
# Enable text search, which will list the commits which match author,
# committer or commit text to a given string. Enabled by default.
@@ -246,28 +301,15 @@ sub feature_blame {
}
sub feature_snapshot {
my ($ctype, $suffix, $command) = @_;
my (@fmts) = @_;
my ($val) = git_get_project_config('snapshot');
if ($val eq 'gzip') {
return ('x-gzip', 'gz', 'gzip');
} elsif ($val eq 'bzip2') {
return ('x-bzip2', 'bz2', 'bzip2');
} elsif ($val eq 'zip') {
return ('x-zip', 'zip', '');
} elsif ($val eq 'none') {
return ();
if ($val) {
@fmts = ($val eq 'none' ? () : split /\s*[,\s]\s*/, $val);
}
return ($ctype, $suffix, $command);
}
sub gitweb_have_snapshot {
my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
my $have_snapshot = (defined $ctype && defined $suffix);
return $have_snapshot;
return @fmts;
}
sub feature_grep {
@@ -310,15 +352,17 @@ sub check_export_ok {
(!$export_ok || -e "$dir/$export_ok"));
}
# rename detection options for git-diff and git-diff-tree
# - default is '-M', with the cost proportional to
# (number of removed files) * (number of new files).
# - more costly is '-C' (or '-C', '-M'), with the cost proportional to
# (number of changed files + number of removed files) * (number of new files)
# - even more costly is '-C', '--find-copies-harder' with cost
# (number of files in the original tree) * (number of new files)
# - one might want to include '-B' option, e.g. '-B', '-M'
our @diff_opts = ('-M'); # taken from git_commit
# process alternate names for backward compatibility
# filter out unsupported (unknown) snapshot formats
sub filter_snapshot_fmts {
my @fmts = @_;
@fmts = map {
exists $known_snapshot_format_aliases{$_} ?
$known_snapshot_format_aliases{$_} : $_} @fmts;
@fmts = grep(exists $known_snapshot_formats{$_}, @fmts);
}
our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
do $GITWEB_CONFIG if -e $GITWEB_CONFIG;
@@ -392,12 +436,11 @@ my %allowed_options = (
our @extra_options = $cgi->param('opt');
if (defined @extra_options) {
foreach(@extra_options)
{
if (not grep(/^$_$/, keys %allowed_options)) {
foreach my $opt (@extra_options) {
if (not exists $allowed_options{$opt}) {
die_error(undef, "Invalid option parameter");
}
if (not grep(/^$action$/, @{$allowed_options{$_}})) {
if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
die_error(undef, "Invalid option parameter for this action");
}
}
@@ -554,7 +597,6 @@ sub href(%) {
action => "a",
file_name => "f",
file_parent => "fp",
extra_options => "opt",
hash => "h",
hash_parent => "hp",
hash_base => "hb",
@@ -563,6 +605,8 @@ sub href(%) {
order => "o",
searchtext => "s",
searchtype => "st",
snapshot_format => "sf",
extra_options => "opt",
);
my %mapping = @mapping;
@@ -585,7 +629,13 @@ sub href(%) {
for (my $i = 0; $i < @mapping; $i += 2) {
my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]);
if (defined $params{$name}) {
push @result, $symbol . "=" . esc_param($params{$name});
if (ref($params{$name}) eq "ARRAY") {
foreach my $par (@{$params{$name}}) {
push @result, $symbol . "=" . esc_param($par);
}
} else {
push @result, $symbol . "=" . esc_param($params{$name});
}
}
}
$href .= "?" . join(';', @result) if scalar @result;
@@ -845,11 +895,25 @@ sub age_string {
return $age_str;
}
use constant {
S_IFINVALID => 0030000,
S_IFGITLINK => 0160000,
};
# submodule/subproject, a commit object reference
sub S_ISGITLINK($) {
my $mode = shift;
return (($mode & S_IFMT) == S_IFGITLINK)
}
# convert file mode in octal to symbolic file mode string
sub mode_str {
my $mode = oct shift;
if (S_ISDIR($mode & S_IFMT)) {
if (S_ISGITLINK($mode)) {
return 'm---------';
} elsif (S_ISDIR($mode & S_IFMT)) {
return 'drwxr-xr-x';
} elsif (S_ISLNK($mode)) {
return 'lrwxrwxrwx';
@@ -875,7 +939,9 @@ sub file_type {
$mode = oct $mode;
}
if (S_ISDIR($mode & S_IFMT)) {
if (S_ISGITLINK($mode)) {
return "submodule";
} elsif (S_ISDIR($mode & S_IFMT)) {
return "directory";
} elsif (S_ISLNK($mode)) {
return "symlink";
@@ -896,7 +962,9 @@ sub file_type_long {
$mode = oct $mode;
}
if (S_ISDIR($mode & S_IFMT)) {
if (S_ISGITLINK($mode)) {
return "submodule";
} elsif (S_ISDIR($mode & S_IFMT)) {
return "directory";
} elsif (S_ISLNK($mode)) {
return "symlink";
@@ -1257,6 +1325,43 @@ sub format_diff_line {
return "<div class=\"diff$diff_class\">" . esc_html($line, -nbsp=>1) . "</div>\n";
}
# Generates undef or something like "_snapshot_" or "snapshot (_tbz2_ _zip_)",
# linked. Pass the hash of the tree/commit to snapshot.
sub format_snapshot_links {
my ($hash) = @_;
my @snapshot_fmts = gitweb_check_feature('snapshot');
@snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
my $num_fmts = @snapshot_fmts;
if ($num_fmts > 1) {
# A parenthesized list of links bearing format names.
# e.g. "snapshot (_tar.gz_ _zip_)"
return "snapshot (" . join(' ', map
$cgi->a({
-href => href(
action=>"snapshot",
hash=>$hash,
snapshot_format=>$_
)
}, $known_snapshot_formats{$_}{'display'})
, @snapshot_fmts) . ")";
} elsif ($num_fmts == 1) {
# A single "snapshot" link whose tooltip bears the format name.
# i.e. "_snapshot_"
my ($fmt) = @snapshot_fmts;
return
$cgi->a({
-href => href(
action=>"snapshot",
hash=>$hash,
snapshot_format=>$fmt
),
-title => "in format: $known_snapshot_formats{$fmt}{'display'}"
}, "snapshot");
} else { # $num_fmts == 0
return undef;
}
}
## ----------------------------------------------------------------------
## git utility subroutines, invoking git commands
@@ -2185,9 +2290,17 @@ EOF
printf('<link rel="alternate" title="%s log RSS feed" '.
'href="%s" type="application/rss+xml" />'."\n",
esc_param($project), href(action=>"rss"));
printf('<link rel="alternate" title="%s log RSS feed (no merges)" '.
'href="%s" type="application/rss+xml" />'."\n",
esc_param($project), href(action=>"rss",
extra_options=>"--no-merges"));
printf('<link rel="alternate" title="%s log Atom feed" '.
'href="%s" type="application/atom+xml" />'."\n",
esc_param($project), href(action=>"atom"));
printf('<link rel="alternate" title="%s log Atom feed (no merges)" '.
'href="%s" type="application/atom+xml" />'."\n",
esc_param($project), href(action=>"atom",
extra_options=>"--no-merges"));
} else {
printf('<link rel="alternate" title="%s projects list" '.
'href="%s" type="text/plain; charset=utf-8"/>'."\n",
@@ -2625,6 +2738,20 @@ sub git_print_tree_entry {
"history");
}
print "</td>\n";
} else {
# unknown object: we can only present history for it
# (this includes 'commit' object, i.e. submodule support)
print "<td class=\"list\">" .
esc_path($t->{'name'}) .
"</td>\n";
print "<td class=\"link\">";
if (defined $hash_base) {
print $cgi->a({-href => href(action=>"history",
hash_base=>$hash_base,
file_name=>"$basedir$t->{'name'}")},
"history");
}
print "</td>\n";
}
}
@@ -3321,8 +3448,6 @@ sub git_shortlog_body {
# uses global variable $project
my ($commitlist, $from, $to, $refs, $extra) = @_;
my $have_snapshot = gitweb_have_snapshot();
$from = 0 unless defined $from;
$to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
@@ -3349,8 +3474,9 @@ sub git_shortlog_body {
$cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
$cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree");
if ($have_snapshot) {
print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot");
my $snapshot_links = format_snapshot_links($commit);
if (defined $snapshot_links) {
print " | " . $snapshot_links;
}
print "</td>\n" .
"</tr>\n";
@@ -4132,8 +4258,6 @@ sub git_blob {
}
sub git_tree {
my $have_snapshot = gitweb_have_snapshot();
if (!defined $hash_base) {
$hash_base = "HEAD";
}
@@ -4167,11 +4291,10 @@ sub git_tree {
hash_base=>"HEAD", file_name=>$file_name)},
"HEAD"),
}
if ($have_snapshot) {
my $snapshot_links = format_snapshot_links($hash);
if (defined $snapshot_links) {
# FIXME: Should be available when we have no hash base as well.
push @views_nav,
$cgi->a({-href => href(action=>"snapshot", hash=>$hash)},
"snapshot");
push @views_nav, $snapshot_links;
}
git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav));
git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
@@ -4235,33 +4358,44 @@ sub git_tree {
}
sub git_snapshot {
my ($ctype, $suffix, $command) = gitweb_check_feature('snapshot');
my $have_snapshot = (defined $ctype && defined $suffix);
if (!$have_snapshot) {
my @supported_fmts = gitweb_check_feature('snapshot');
@supported_fmts = filter_snapshot_fmts(@supported_fmts);
my $format = $cgi->param('sf');
if (!@supported_fmts) {
die_error('403 Permission denied', "Permission denied");
}
# default to first supported snapshot format
$format ||= $supported_fmts[0];
if ($format !~ m/^[a-z0-9]+$/) {
die_error(undef, "Invalid snapshot format parameter");
} elsif (!exists($known_snapshot_formats{$format})) {
die_error(undef, "Unknown snapshot format");
} elsif (!grep($_ eq $format, @supported_fmts)) {
die_error(undef, "Unsupported snapshot format");
}
if (!defined $hash) {
$hash = git_get_head_hash($project);
}
my $git = git_cmd_str();
my $git_command = git_cmd_str();
my $name = $project;
$name =~ s,([^/])/*\.git$,$1,;
$name = basename($name);
my $filename = to_utf8($name);
$name =~ s/\047/\047\\\047\047/g;
my $cmd;
if ($suffix eq 'zip') {
$filename .= "-$hash.$suffix";
$cmd = "$git archive --format=zip --prefix=\'$name\'/ $hash";
} else {
$filename .= "-$hash.tar.$suffix";
$cmd = "$git archive --format=tar --prefix=\'$name\'/ $hash | $command";
$filename .= "-$hash$known_snapshot_formats{$format}{'suffix'}";
$cmd = "$git_command archive " .
"--format=$known_snapshot_formats{$format}{'format'} " .
"--prefix=\'$name\'/ $hash";
if (exists $known_snapshot_formats{$format}{'compressor'}) {
$cmd .= ' | ' . join ' ', @{$known_snapshot_formats{$format}{'compressor'}};
}
print $cgi->header(
-type => "application/$ctype",
-type => $known_snapshot_formats{$format}{'type'},
-content_disposition => 'inline; filename="' . "$filename" . '"',
-status => '200 OK');
@@ -4271,7 +4405,6 @@ sub git_snapshot {
print <$fd>;
binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
close $fd;
}
sub git_log {
@@ -4390,8 +4523,6 @@ sub git_commit {
my $refs = git_get_references();
my $ref = format_ref_marker($refs, $co{'id'});
my $have_snapshot = gitweb_have_snapshot();
git_header_html(undef, $expires);
git_print_page_nav('commit', '',
$hash, $co{'tree'}, $hash,
@@ -4430,9 +4561,9 @@ sub git_commit {
"<td class=\"link\">" .
$cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash)},
"tree");
if ($have_snapshot) {
print " | " .
$cgi->a({-href => href(action=>"snapshot", hash=>$hash)}, "snapshot");
my $snapshot_links = format_snapshot_links($hash);
if (defined $snapshot_links) {
print " | " . $snapshot_links;
}
print "</td>" .
"</tr>\n";

View File

@@ -29,6 +29,19 @@ static void remove_lock_file_on_signal(int signo)
static int lock_file(struct lock_file *lk, const char *path)
{
struct stat st;
if ((!lstat(path, &st)) && S_ISLNK(st.st_mode)) {
ssize_t sz;
static char target[PATH_MAX];
sz = readlink(path, target, sizeof(target));
if (sz < 0)
warning("Cannot readlink %s", path);
else if (target[0] != '/')
warning("Cannot lock target of relative symlink %s", path);
else
path = target;
}
sprintf(lk->filename, "%s.lock", path);
lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
if (0 <= lk->fd) {

View File

@@ -120,7 +120,7 @@ int main(int argc, char **argv)
setup_git_directory();
if (read_pipe(0, &buffer, &size)) {
if (read_fd(0, &buffer, &size)) {
free(buffer);
die("could not read from stdin");
}

30
path.c
View File

@@ -71,24 +71,22 @@ char *git_path(const char *fmt, ...)
/* git_mkstemp() - create tmp file honoring TMPDIR variable */
int git_mkstemp(char *path, size_t len, const char *template)
{
char *env, *pch = path;
const char *tmp;
size_t n;
if ((env = getenv("TMPDIR")) == NULL &&
/* on Windows it is TMP and TEMP */
(env = getenv("TMP")) == NULL &&
(env = getenv("TEMP")) == NULL) {
strcpy(pch, "/tmp/");
len -= 5;
pch += 5;
} else {
size_t n = snprintf(pch, len, "%s/", env);
len -= n;
pch += n;
tmp = getenv("TMPDIR");
/* on Windows it is TMP and TEMP */
if (!tmp)
tmp = getenv("TMP");
if (!tmp)
tmp = getenv("TEMP");
if (!tmp)
tmp = "/tmp";
n = snprintf(path, len, "%s/%s", tmp, template);
if (len <= n) {
errno = ENAMETOOLONG;
return -1;
}
strlcpy(pch, template, len);
return mkstemp(path);
}

View File

@@ -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)

View File

@@ -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);

View File

@@ -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 */

View File

@@ -2314,27 +2314,36 @@ int has_sha1_file(const unsigned char *sha1)
*
* returns 0 if anything went fine and -1 otherwise
*
* The buffer is always NUL-terminated, not including it in returned size.
*
* NOTE: both buf and size may change, but even when -1 is returned
* you still have to free() it yourself.
*/
int read_pipe(int fd, char** return_buf, unsigned long* return_size)
int read_fd(int fd, char **return_buf, unsigned long *return_size)
{
char* buf = *return_buf;
char *buf = *return_buf;
unsigned long size = *return_size;
ssize_t iret;
unsigned long off = 0;
if (!buf || size <= 1) {
size = 1024;
buf = xrealloc(buf, size);
}
do {
iret = xread(fd, buf + off, size - off);
iret = xread(fd, buf + off, (size - 1) - off);
if (iret > 0) {
off += iret;
if (off == size) {
size *= 2;
if (off == size - 1) {
size = alloc_nr(size);
buf = xrealloc(buf, size);
}
}
} while (iret > 0);
buf[off] = '\0';
*return_buf = buf;
*return_size = off;
@@ -2349,7 +2358,7 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
char *buf = xmalloc(size);
int ret;
if (read_pipe(fd, &buf, &size)) {
if (read_fd(fd, &buf, &size)) {
free(buf);
return -1;
}

View File

@@ -48,3 +48,37 @@ svnrepo="file://$svnrepo"
poke() {
test-chmtime +1 "$1"
}
SVN_HTTPD_MODULE_PATH=${SVN_HTTPD_MODULE_PATH-'/usr/lib/apache2/modules'}
SVN_HTTPD_PATH=${SVN_HTTPD_PATH-'/usr/sbin/apache2'}
start_httpd () {
if test -z "$SVN_HTTPD_PORT"
then
echo >&2 'SVN_HTTPD_PORT is not defined!'
return
fi
mkdir "$GIT_DIR"/logs
cat > "$GIT_DIR/httpd.conf" <<EOF
ServerName "git-svn test"
ServerRoot "$GIT_DIR"
DocumentRoot "$GIT_DIR"
PidFile "$GIT_DIR/httpd.pid"
Listen 127.0.0.1:$SVN_HTTPD_PORT
LoadModule dav_module $SVN_HTTPD_MODULE_PATH/mod_dav.so
LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so
<Location /svn>
DAV svn
SVNPath $rawsvnrepo
</Location>
EOF
"$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start
svnrepo=http://127.0.0.1:$SVN_HTTPD_PORT/svn
}
stop_httpd () {
test -z "$SVN_HTTPD_PORT" && return
"$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop
}

View File

@@ -65,6 +65,7 @@ cat > fake-editor.sh << EOF
#!/bin/sh
test "\$1" = .git/COMMIT_EDITMSG && {
test -z "\$FAKE_COMMIT_MESSAGE" || echo "\$FAKE_COMMIT_MESSAGE" > "\$1"
test -z "\$FAKE_COMMIT_AMEND" || echo "\$FAKE_COMMIT_AMEND" >> "\$1"
exit
}
test -z "\$FAKE_LINES" && exit
@@ -212,4 +213,42 @@ test_expect_success 'verbose flag is heeded, even after --continue' '
grep "^ file1 | 2 +-$" output
'
test_expect_success 'multi-squash only fires up editor once' '
base=$(git rev-parse HEAD~4) &&
FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \
git rebase -i $base &&
test $base = $(git rev-parse HEAD^) &&
test 1 = $(git show | grep ONCE | wc -l)
'
test_expect_success 'squash works as expected' '
for n in one two three four
do
echo $n >> file$n &&
git add file$n &&
git commit -m $n
done &&
one=$(git rev-parse HEAD~3) &&
FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
test $one = $(git rev-parse HEAD~2)
'
test_expect_success 'interrupted squash works as expected' '
for n in one two three four
do
echo $n >> conflict &&
git add conflict &&
git commit -m $n
done &&
one=$(git rev-parse HEAD~3) &&
! FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
(echo one; echo two; echo four) > conflict &&
git add conflict &&
! git rebase --continue &&
echo resolved > conflict &&
git add conflict &&
git rebase --continue &&
test $one = $(git rev-parse HEAD~2)
'
test_done

View File

@@ -66,4 +66,11 @@ test_expect_success 'apply stashed changes (including index)' '
test 1 = $(git show HEAD:file)
'
test_expect_success 'unstashing in a subdirectory' '
git reset --hard HEAD &&
mkdir subdir &&
cd subdir &&
git stash apply
'
test_done

View File

@@ -30,24 +30,24 @@ test_expect_success 'setup' '
H=$(git rev-parse H)
test_expect_success 'rewrite identically' '
git-filter-branch H2
git-filter-branch branch
'
test_expect_success 'result is really identical' '
test $H = $(git rev-parse H2)
test $H = $(git rev-parse HEAD)
'
test_expect_success 'rewrite, renaming a specific file' '
git-filter-branch --tree-filter "mv d doh || :" H3
git-filter-branch -f --tree-filter "mv d doh || :" HEAD
'
test_expect_success 'test that the file was renamed' '
test d = $(git show H3:doh)
test d = $(git show HEAD:doh)
'
git tag oldD H3~4
git tag oldD HEAD~4
test_expect_success 'rewrite one branch, keeping a side branch' '
git-filter-branch --tree-filter "mv b boh || :" modD D..oldD
git branch modD oldD &&
git-filter-branch -f --tree-filter "mv b boh || :" D..modD
'
test_expect_success 'common ancestor is still common (unchanged)' '
@@ -69,7 +69,8 @@ test_expect_success 'filter subdirectory only' '
git rm a &&
test_tick &&
git commit -m "again not subdir" &&
git-filter-branch --subdirectory-filter subdir sub
git branch sub &&
git-filter-branch -f --subdirectory-filter subdir refs/heads/sub
'
test_expect_success 'subdirectory filter result looks okay' '
@@ -89,7 +90,8 @@ test_expect_success 'setup and filter history that requires --full-history' '
test_tick &&
git commit -m "again subdir on master" &&
git merge branch &&
git-filter-branch --subdirectory-filter subdir sub-master
git branch sub-master &&
git-filter-branch -f --subdirectory-filter subdir sub-master
'
test_expect_success 'subdirectory filter result looks okay' '
@@ -100,7 +102,8 @@ test_expect_success 'subdirectory filter result looks okay' '
'
test_expect_success 'use index-filter to move into a subdirectory' '
git-filter-branch --index-filter \
git branch directorymoved &&
git-filter-branch -f --index-filter \
"git ls-files -s | sed \"s-\\t-&newsubdir/-\" |
GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \
git update-index --index-info &&
@@ -108,9 +111,10 @@ test_expect_success 'use index-filter to move into a subdirectory' '
test -z "$(git diff HEAD directorymoved:newsubdir)"'
test_expect_success 'stops when msg filter fails' '
! git-filter-branch --msg-filter false nonono &&
rm -rf .git-rewrite &&
! git rev-parse nonono
old=$(git rev-parse HEAD) &&
! git-filter-branch -f --msg-filter false &&
test $old = $(git rev-parse HEAD) &&
rm -rf .git-rewrite
'
test_expect_success 'author information is preserved' '
@@ -118,7 +122,8 @@ test_expect_success 'author information is preserved' '
git add i &&
test_tick &&
GIT_AUTHOR_NAME="B V Uips" git commit -m bvuips &&
git-filter-branch --msg-filter "cat; \
git branch preserved-author &&
git-filter-branch -f --msg-filter "cat; \
test \$GIT_COMMIT != $(git rev-parse master) || \
echo Hallo" \
preserved-author &&
@@ -129,7 +134,8 @@ test_expect_success "remove a certain author's commits" '
echo i > i &&
test_tick &&
git commit -m i i &&
git-filter-branch --commit-filter "\
git branch removed-author &&
git-filter-branch -f --commit-filter "\
if [ \"\$GIT_AUTHOR_NAME\" = \"B V Uips\" ];\
then\
shift;\
@@ -148,4 +154,9 @@ test_expect_success "remove a certain author's commits" '
test 0 = $(git rev-list --author="B V Uips" removed-author | wc -l)
'
test_expect_success 'barf on invalid name' '
! git filter-branch -f master xy-problem &&
! git filter-branch -f HEAD^
'
test_done

91
t/t7005-editor.sh Executable file
View File

@@ -0,0 +1,91 @@
#!/bin/sh
test_description='GIT_EDITOR, core.editor, and stuff'
. ./test-lib.sh
for i in GIT_EDITOR core_editor EDITOR VISUAL vi
do
cat >e-$i.sh <<-EOF
echo "Edited by $i" >"\$1"
EOF
chmod +x e-$i.sh
done
unset vi
mv e-vi.sh vi
PATH=".:$PATH"
unset EDITOR VISUAL GIT_EDITOR
test_expect_success setup '
msg="Hand edited" &&
echo "$msg" >expect &&
git add vi &&
test_tick &&
git commit -m "$msg" &&
git show -s --pretty=oneline |
sed -e "s/^[0-9a-f]* //" >actual &&
diff actual expect
'
TERM=dumb
export TERM
test_expect_success 'dumb should error out when falling back on vi' '
if git commit --amend
then
echo "Oops?"
exit 1
else
: happy
fi
'
TERM=vt100
export TERM
for i in vi EDITOR VISUAL core_editor GIT_EDITOR
do
echo "Edited by $i" >expect
unset EDITOR VISUAL GIT_EDITOR
git config --unset-all core.editor
case "$i" in
core_editor)
git config core.editor ./e-core_editor.sh
;;
[A-Z]*)
eval "$i=./e-$i.sh"
export $i
;;
esac
test_expect_success "Using $i" '
git commit --amend &&
git show -s --pretty=oneline |
sed -e "s/^[0-9a-f]* //" >actual &&
diff actual expect
'
done
unset EDITOR VISUAL GIT_EDITOR
git config --unset-all core.editor
for i in vi EDITOR VISUAL core_editor GIT_EDITOR
do
echo "Edited by $i" >expect
case "$i" in
core_editor)
git config core.editor ./e-core_editor.sh
;;
[A-Z]*)
eval "$i=./e-$i.sh"
export $i
;;
esac
test_expect_success "Using $i (override)" '
git commit --amend &&
git show -s --pretty=oneline |
sed -e "s/^[0-9a-f]* //" >actual &&
diff actual expect
'
done
test_done

View File

@@ -21,6 +21,10 @@ subcommands of git-submodule.
# -add an entry to .gitmodules for submodule 'example'
#
test_expect_success 'Prepare submodule testing' '
: > t &&
git-add t &&
git-commit -m "initial commit" &&
git branch initial HEAD &&
mkdir lib &&
cd lib &&
git init &&
@@ -166,4 +170,9 @@ test_expect_success 'status should be "up-to-date" after update' '
git-submodule status | grep "^ $rev1"
'
test_expect_success 'checkout superproject with subproject already present' '
git-checkout initial &&
git-checkout master
'
test_done

96
t/t7500-commit.sh Executable file
View File

@@ -0,0 +1,96 @@
#!/bin/sh
#
# Copyright (c) 2007 Steven Grimm
#
test_description='git-commit
Tests for selected commit options.'
. ./test-lib.sh
commit_msg_is () {
test "`git log --pretty=format:%s%b -1`" = "$1"
}
# A sanity check to see if commit is working at all.
test_expect_success 'a basic commit in an empty tree should succeed' '
echo content > foo &&
git add foo &&
git commit -m "initial commit"
'
test_expect_success 'nonexistent template file should return error' '
echo changes >> foo &&
git add foo &&
! git commit --template "$PWD"/notexist
'
test_expect_success 'nonexistent template file in config should return error' '
git config commit.template "$PWD"/notexist &&
! git commit &&
git config --unset commit.template
'
# From now on we'll use a template file that exists.
TEMPLATE="$PWD"/template
test_expect_success 'unedited template should not commit' '
echo "template line" > "$TEMPLATE" &&
! git commit --template "$TEMPLATE"
'
test_expect_success 'unedited template with comments should not commit' '
echo "# comment in template" >> "$TEMPLATE" &&
! git commit --template "$TEMPLATE"
'
test_expect_success 'a Signed-off-by line by itself should not commit' '
! GIT_EDITOR=../t7500/add-signed-off git commit --template "$TEMPLATE"
'
test_expect_success 'adding comments to a template should not commit' '
! GIT_EDITOR=../t7500/add-comments git commit --template "$TEMPLATE"
'
test_expect_success 'adding real content to a template should commit' '
GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" &&
commit_msg_is "template linecommit message"
'
test_expect_success '-t option should be short for --template' '
echo "short template" > "$TEMPLATE" &&
echo "new content" >> foo &&
git add foo &&
GIT_EDITOR=../t7500/add-content git commit -t "$TEMPLATE" &&
commit_msg_is "short templatecommit message"
'
test_expect_success 'config-specified template should commit' '
echo "new template" > "$TEMPLATE" &&
git config commit.template "$TEMPLATE" &&
echo "more content" >> foo &&
git add foo &&
GIT_EDITOR=../t7500/add-content git commit &&
git config --unset commit.template &&
commit_msg_is "new templatecommit message"
'
test_expect_success 'explicit commit message should override template' '
echo "still more content" >> foo &&
git add foo &&
GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" \
-m "command line msg" &&
commit_msg_is "command line msg<unknown>"
'
test_expect_success 'commit message from file should override template' '
echo "content galore" >> foo &&
git add foo &&
echo "standard input msg" |
GIT_EDITOR=../t7500/add-content git commit \
--template "$TEMPLATE" --file - &&
commit_msg_is "standard input msg<unknown>"
'
test_done

4
t/t7500/add-comments Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
echo "# this is a new comment" >> "$1"
echo "# and so is this" >> "$1"
exit 0

3
t/t7500/add-content Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
echo "commit message" >> "$1"
exit 0

3
t/t7500/add-signed-off Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
echo "Signed-off-by: foo <bar@frotz>" >> "$1"
exit 0

View File

@@ -0,0 +1,54 @@
#!/bin/sh
#
# Copyright (c) 2007 Eric Wong
test_description='git-svn dcommit can commit renames of files with ugly names'
. ./lib-git-svn.sh
test_expect_success 'load repository with strange names' "
svnadmin load -q $rawsvnrepo < ../t9115/funky-names.dump &&
start_httpd
"
test_expect_success 'init and fetch repository' "
git svn init $svnrepo &&
git svn fetch &&
git reset --hard git-svn
"
test_expect_success 'create file in existing ugly and empty dir' '
mkdir "#{bad_directory_name}" &&
echo hi > "#{bad_directory_name}/ foo" &&
git update-index --add "#{bad_directory_name}/ foo" &&
git commit -m "new file in ugly parent" &&
git svn dcommit
'
test_expect_success 'rename ugly file' '
git mv "#{bad_directory_name}/ foo" "file name with feces" &&
git commit -m "rename ugly file" &&
git svn dcommit
'
test_expect_success 'rename pretty file' '
echo :x > pretty &&
git update-index --add pretty &&
git commit -m "pretty :x" &&
git svn dcommit &&
mkdir regular_dir_name &&
git mv pretty regular_dir_name/pretty &&
git commit -m "moved pretty file" &&
git svn dcommit
'
test_expect_success 'rename pretty file into ugly one' '
git mv regular_dir_name/pretty "#{bad_directory_name}/ booboo" &&
git commit -m booboo &&
git svn dcommit
'
stop_httpd
test_done

103
t/t9115/funky-names.dump Normal file
View File

@@ -0,0 +1,103 @@
SVN-fs-dump-format-version: 2
UUID: 819c44fe-2bcc-4066-88e4-985e2bc0b418
Revision-number: 0
Prop-content-length: 56
Content-length: 56
K 8
svn:date
V 27
2007-07-12T07:54:26.062914Z
PROPS-END
Revision-number: 1
Prop-content-length: 152
Content-length: 152
K 7
svn:log
V 44
what will those wacky people think of next?
K 10
svn:author
V 12
normalperson
K 8
svn:date
V 27
2007-07-12T08:00:05.011573Z
PROPS-END
Node-path: leading space
Node-kind: dir
Node-action: add
Prop-content-length: 10
Content-length: 10
PROPS-END
Node-path: leading space file
Node-kind: file
Node-action: add
Prop-content-length: 10
Text-content-length: 5
Text-content-md5: e4fa20c67542cdc21271e08d329397ab
Content-length: 15
PROPS-END
ugly
Node-path: #{bad_directory_name}
Node-kind: dir
Node-action: add
Prop-content-length: 10
Content-length: 10
PROPS-END
Node-path: #{cool_name}
Node-kind: file
Node-action: add
Prop-content-length: 10
Text-content-length: 18
Text-content-md5: 87dac40ca337dfa3dcc8911388c3ddda
Content-length: 28
PROPS-END
strange name here
Node-path: dir name with spaces
Node-kind: dir
Node-action: add
Prop-content-length: 10
Content-length: 10
PROPS-END
Node-path: file name with spaces
Node-kind: file
Node-action: add
Prop-content-length: 10
Text-content-length: 7
Text-content-md5: c1f10cfd640618484a2a475c11410fd3
Content-length: 17
PROPS-END
spaces
Node-path: regular_dir_name
Node-kind: dir
Node-action: add
Prop-content-length: 10
Content-length: 10
PROPS-END

View File

@@ -28,6 +28,18 @@ git add empty &&
git commit -q -a -m "Initial" 2>/dev/null ||
exit 1
check_entries () {
# $1 == directory, $2 == expected
grep '^/' "$1/CVS/Entries" | /usr/bin/sort | cut -d/ -f2,3,5 >actual
if test -z "$2"
then
>expected
else
printf '%s\n' "$2" | tr '|' '\012' >expected
fi
diff -u expected actual
}
test_expect_success \
'New file' \
'mkdir A B C D E F &&
@@ -43,10 +55,10 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git cvsexportcommit -c $id &&
test "$(echo $(/usr/bin/sort A/CVS/Entries|cut -d/ -f2,3,5))" = "newfile1.txt/1.1/" &&
test "$(echo $(/usr/bin/sort B/CVS/Entries|cut -d/ -f2,3,5))" = "newfile2.txt/1.1/" &&
test "$(echo $(/usr/bin/sort C/CVS/Entries|cut -d/ -f2,3,5))" = "newfile3.png/1.1/-kb" &&
test "$(echo $(/usr/bin/sort D/CVS/Entries|cut -d/ -f2,3,5))" = "newfile4.png/1.1/-kb" &&
check_entries A "newfile1.txt/1.1/" &&
check_entries B "newfile2.txt/1.1/" &&
check_entries C "newfile3.png/1.1/-kb" &&
check_entries D "newfile4.png/1.1/-kb" &&
diff A/newfile1.txt ../A/newfile1.txt &&
diff B/newfile2.txt ../B/newfile2.txt &&
diff C/newfile3.png ../C/newfile3.png &&
@@ -67,12 +79,12 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git cvsexportcommit -c $id &&
test "$(echo $(/usr/bin/sort A/CVS/Entries|cut -d/ -f2,3,5))" = "newfile1.txt/1.2/" &&
test "$(echo $(/usr/bin/sort B/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
test "$(echo $(/usr/bin/sort C/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
test "$(echo $(/usr/bin/sort D/CVS/Entries|cut -d/ -f2,3,5))" = "newfile4.png/1.2/-kb" &&
test "$(echo $(/usr/bin/sort E/CVS/Entries|cut -d/ -f2,3,5))" = "newfile5.txt/1.1/" &&
test "$(echo $(/usr/bin/sort F/CVS/Entries|cut -d/ -f2,3,5))" = "newfile6.png/1.1/-kb" &&
check_entries A "newfile1.txt/1.2/" &&
check_entries B "" &&
check_entries C "" &&
check_entries D "newfile4.png/1.2/-kb" &&
check_entries E "newfile5.txt/1.1/" &&
check_entries F "newfile6.png/1.1/-kb" &&
diff A/newfile1.txt ../A/newfile1.txt &&
diff D/newfile4.png ../D/newfile4.png &&
diff E/newfile5.txt ../E/newfile5.txt &&
@@ -115,12 +127,12 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git cvsexportcommit -c $id &&
test "$(echo $(/usr/bin/sort A/CVS/Entries|cut -d/ -f2,3,5))" = "newfile1.txt/1.2/" &&
test "$(echo $(/usr/bin/sort B/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
test "$(echo $(/usr/bin/sort C/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
test "$(echo $(/usr/bin/sort D/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
test "$(echo $(/usr/bin/sort E/CVS/Entries|cut -d/ -f2,3,5))" = "newfile5.txt/1.1/" &&
test "$(echo $(/usr/bin/sort F/CVS/Entries|cut -d/ -f2,3,5))" = "newfile6.png/1.1/-kb" &&
check_entries A "newfile1.txt/1.2/" &&
check_entries B "" &&
check_entries C "" &&
check_entries D "" &&
check_entries E "newfile5.txt/1.1/" &&
check_entries F "newfile6.png/1.1/-kb" &&
diff A/newfile1.txt ../A/newfile1.txt &&
diff E/newfile5.txt ../E/newfile5.txt &&
diff F/newfile6.png ../F/newfile6.png
@@ -133,12 +145,12 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git cvsexportcommit -c $id &&
test "$(echo $(/usr/bin/sort A/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
test "$(echo $(/usr/bin/sort B/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
test "$(echo $(/usr/bin/sort C/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
test "$(echo $(/usr/bin/sort D/CVS/Entries|cut -d/ -f2,3,5))" = "" &&
test "$(echo $(/usr/bin/sort E/CVS/Entries|cut -d/ -f2,3,5))" = "newfile5.txt/1.1/" &&
test "$(echo $(/usr/bin/sort F/CVS/Entries|cut -d/ -f2,3,5))" = "newfile6.png/1.1/-kb" &&
check_entries A "" &&
check_entries B "" &&
check_entries C "" &&
check_entries D "" &&
check_entries E "newfile5.txt/1.1/" &&
check_entries F "newfile6.png/1.1/-kb" &&
diff E/newfile5.txt ../E/newfile5.txt &&
diff F/newfile6.png ../F/newfile6.png
)'
@@ -154,7 +166,7 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git-cvsexportcommit -c $id &&
test "$(echo $(/usr/bin/sort "G g/CVS/Entries"|cut -d/ -f2,3,5))" = "with spaces.png/1.1/-kb with spaces.txt/1.1/"
check_entries "G g" "with spaces.png/1.1/-kb|with spaces.txt/1.1/"
)'
test_expect_success \
@@ -166,7 +178,7 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git-cvsexportcommit -c $id
test "$(echo $(/usr/bin/sort "G g/CVS/Entries"|cut -d/ -f2,3,5))" = "with spaces.png/1.2/-kb with spaces.txt/1.2/"
check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
)'
# Some filesystems mangle pathnames with UTF-8 characters --
@@ -191,7 +203,9 @@ test_expect_success \
id=$(git rev-list --max-count=1 HEAD) &&
(cd "$CVSWORK" &&
git-cvsexportcommit -v -c $id &&
test "$(echo $(/usr/bin/sortsort Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/CVS/Entries|cut -d/ -f2,3,5))" = "gårdetsågårdet.png/1.1/-kb gårdetsågårdet.txt/1.1/"
check_entries \
"Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" \
"gårdetsågårdet.png/1.1/-kb|gårdetsågårdet.txt/1.1/"
)'
fi

View File

@@ -577,7 +577,7 @@ EXPECT_END
test_expect_success \
'L: verify internal tree sorting' \
'git-fast-import <input &&
git diff --raw L^ L >output &&
git diff-tree --abbrev --raw L^ L >output &&
git diff expect output'
###

View File

@@ -521,4 +521,32 @@ test_expect_success \
'gitweb_run "p=.git;a=log"'
test_debug 'cat gitweb.log'
# ----------------------------------------------------------------------
# extra options
test_expect_success \
'opt: log --no-merges' \
'gitweb_run "p=.git;a=log;opt=--no-merges"'
test_debug 'cat gitweb.log'
test_expect_success \
'opt: atom --no-merges' \
'gitweb_run "p=.git;a=log;opt=--no-merges"'
test_debug 'cat gitweb.log'
test_expect_success \
'opt: "file" history --no-merges' \
'gitweb_run "p=.git;a=history;f=file;opt=--no-merges"'
test_debug 'cat gitweb.log'
test_expect_success \
'opt: log --no-such-option (invalid option)' \
'gitweb_run "p=.git;a=log;opt=--no-such-option"'
test_debug 'cat gitweb.log'
test_expect_success \
'opt: tree --no-merges (invalid option for action)' \
'gitweb_run "p=.git;a=tree;opt=--no-merges"'
test_debug 'cat gitweb.log'
test_done

View File

@@ -5,14 +5,12 @@
#include "cache-tree.h"
#include "unpack-trees.h"
#include "progress.h"
#include "refs.h"
#define DBRT_DEBUG 1
struct tree_entry_list {
struct tree_entry_list *next;
unsigned directory : 1;
unsigned executable : 1;
unsigned symlink : 1;
unsigned int mode;
const char *name;
const unsigned char *sha1;
@@ -37,9 +35,6 @@ static struct tree_entry_list *create_tree_entry_list(struct tree *tree)
entry->name = one.path;
entry->sha1 = one.sha1;
entry->mode = one.mode;
entry->directory = S_ISDIR(one.mode) != 0;
entry->executable = (one.mode & S_IXUSR) != 0;
entry->symlink = S_ISLNK(one.mode) != 0;
entry->next = NULL;
*list_p = entry;
@@ -140,9 +135,9 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
#endif
if (!first || entcmp(first, firstdir,
posns[i]->name,
posns[i]->directory) > 0) {
S_ISDIR(posns[i]->mode)) > 0) {
first = posns[i]->name;
firstdir = posns[i]->directory;
firstdir = S_ISDIR(posns[i]->mode);
}
}
/* No name means we're done */
@@ -176,7 +171,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
continue;
}
if (posns[i]->directory) {
if (S_ISDIR(posns[i]->mode)) {
struct tree *tree = lookup_tree(posns[i]->sha1);
any_dirs = 1;
parse_tree(tree);
@@ -425,11 +420,24 @@ static void invalidate_ce_path(struct cache_entry *ce)
cache_tree_invalidate_path(active_cache_tree, ce->name);
}
static int verify_clean_subdirectory(const char *path, const char *action,
/*
* Check that checking out ce->sha1 in subdir ce->name is not
* going to overwrite any working files.
*
* Currently, git does not checkout subprojects during a superproject
* checkout, so it is not going to overwrite anything.
*/
static int verify_clean_submodule(struct cache_entry *ce, const char *action,
struct unpack_trees_options *o)
{
return 0;
}
static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
struct unpack_trees_options *o)
{
/*
* we are about to extract "path"; we would not want to lose
* we are about to extract "ce->name"; we would not want to lose
* anything in the existing directory there.
*/
int namelen;
@@ -437,13 +445,24 @@ static int verify_clean_subdirectory(const char *path, const char *action,
struct dir_struct d;
char *pathbuf;
int cnt = 0;
unsigned char sha1[20];
if (S_ISGITLINK(ntohl(ce->ce_mode)) &&
resolve_gitlink_ref(ce->name, "HEAD", sha1) == 0) {
/* If we are not going to update the submodule, then
* we don't care.
*/
if (!hashcmp(sha1, ce->sha1))
return 0;
return verify_clean_submodule(ce, action, o);
}
/*
* First let's make sure we do not have a local modification
* in that directory.
*/
namelen = strlen(path);
pos = cache_name_pos(path, namelen);
namelen = strlen(ce->name);
pos = cache_name_pos(ce->name, namelen);
if (0 <= pos)
return cnt; /* we have it as nondirectory */
pos = -pos - 1;
@@ -451,7 +470,7 @@ static int verify_clean_subdirectory(const char *path, const char *action,
struct cache_entry *ce = active_cache[i];
int len = ce_namelen(ce);
if (len < namelen ||
strncmp(path, ce->name, namelen) ||
strncmp(ce->name, ce->name, namelen) ||
ce->name[namelen] != '/')
break;
/*
@@ -469,16 +488,16 @@ static int verify_clean_subdirectory(const char *path, const char *action,
* present file that is not ignored.
*/
pathbuf = xmalloc(namelen + 2);
memcpy(pathbuf, path, namelen);
memcpy(pathbuf, ce->name, namelen);
strcpy(pathbuf+namelen, "/");
memset(&d, 0, sizeof(d));
if (o->dir)
d.exclude_per_dir = o->dir->exclude_per_dir;
i = read_directory(&d, path, pathbuf, namelen+1, NULL);
i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL);
if (i)
die("Updating '%s' would lose untracked files in it",
path);
ce->name);
free(pathbuf);
return cnt;
}
@@ -487,7 +506,7 @@ static int verify_clean_subdirectory(const char *path, const char *action,
* We do not want to remove or overwrite a working tree file that
* is not tracked, unless it is ignored.
*/
static void verify_absent(const char *path, const char *action,
static void verify_absent(struct cache_entry *ce, const char *action,
struct unpack_trees_options *o)
{
struct stat st;
@@ -495,15 +514,15 @@ static void verify_absent(const char *path, const char *action,
if (o->index_only || o->reset || !o->update)
return;
if (has_symlink_leading_path(path, NULL))
if (has_symlink_leading_path(ce->name, NULL))
return;
if (!lstat(path, &st)) {
if (!lstat(ce->name, &st)) {
int cnt;
if (o->dir && excluded(o->dir, path))
if (o->dir && excluded(o->dir, ce->name))
/*
* path is explicitly excluded, so it is Ok to
* ce->name is explicitly excluded, so it is Ok to
* overwrite it.
*/
return;
@@ -515,7 +534,7 @@ static void verify_absent(const char *path, const char *action,
* files that are in "foo/" we would lose
* it.
*/
cnt = verify_clean_subdirectory(path, action, o);
cnt = verify_clean_subdirectory(ce, action, o);
/*
* If this removed entries from the index,
@@ -543,7 +562,7 @@ static void verify_absent(const char *path, const char *action,
* delete this path, which is in a subdirectory that
* is being replaced with a blob.
*/
cnt = cache_name_pos(path, strlen(path));
cnt = cache_name_pos(ce->name, strlen(ce->name));
if (0 <= cnt) {
struct cache_entry *ce = active_cache[cnt];
if (!ce_stage(ce) && !ce->ce_mode)
@@ -551,7 +570,7 @@ static void verify_absent(const char *path, const char *action,
}
die("Untracked working tree file '%s' "
"would be %s by merge.", path, action);
"would be %s by merge.", ce->name, action);
}
}
@@ -575,7 +594,7 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
}
}
else {
verify_absent(merge->name, "overwritten", o);
verify_absent(merge, "overwritten", o);
invalidate_ce_path(merge);
}
@@ -590,7 +609,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
if (old)
verify_uptodate(old, o);
else
verify_absent(ce->name, "removed", o);
verify_absent(ce, "removed", o);
ce->ce_mode = 0;
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
invalidate_ce_path(ce);
@@ -707,18 +726,18 @@ int threeway_merge(struct cache_entry **stages,
if (o->aggressive) {
int head_deleted = !head && !df_conflict_head;
int remote_deleted = !remote && !df_conflict_remote;
const char *path = NULL;
struct cache_entry *ce = NULL;
if (index)
path = index->name;
ce = index;
else if (head)
path = head->name;
ce = head;
else if (remote)
path = remote->name;
ce = remote;
else {
for (i = 1; i < o->head_idx; i++) {
if (stages[i] && stages[i] != o->df_conflict_entry) {
path = stages[i]->name;
ce = stages[i];
break;
}
}
@@ -733,8 +752,8 @@ int threeway_merge(struct cache_entry **stages,
(remote_deleted && head && head_match)) {
if (index)
return deleted_entry(index, index, o);
else if (path && !head_deleted)
verify_absent(path, "removed", o);
else if (ce && !head_deleted)
verify_absent(ce, "removed", o);
return 0;
}
/*