Its wrong to exit the application if we destroy a random widget
contained withing something else; especially if its some small
trivial thing that has no impact on the overall operation.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
To improve performance on fork+exec impoverished systems (such as
Windows) we want to avoid running git-symbolic-ref on every rescan
if we can do so. A quick way to implement such an avoidance is to
just read the HEAD ref ourselves; we'll either see it as a symref
(starts with "ref: ") or we'll see it as a detached head (40 hex
digits). In either case we can treat that as our current branch.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Johannes Sixt pointed out that dropping to 0 lines of context
does allow the user to get more fine-grained hunk selection,
especially since we don't currently support "highlight and
apply (or revert)".
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
If the user has no branches at all (their refs/heads/ is empty)
and they are on a detached HEAD we have a valid repository but
there are no branches to populate into the branch pulldown in
the create branch dialog. Instead of erroring out we can skip
that part of the dialog, much like we do with tracking branches
or tags when the user doesn't have any.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Recently git-merge learned to avoid generating the diffstat after
a merge by reading the merge.diffstat configuration option. By
default this option is assumed to be true, as that is the old
behavior. However we can force it to false by setting it as a
standard boolean option.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Git has supported remote branch deletion for quite some time, but
I've just never gotten around to supporting it in git-gui. Some
workflows have users push short-term branches to some remote Git
repository, then delete them a few days/weeks later when that topic
has been fully merged into the main trunk. Typically in that style
of workflow the user will want to remove the branches they created.
We now offer a "Delete..." option in the Push menu, right below the
generic "Push..." option. When the user opens our generic delete
dialog they can select a preconfigured remote, or enter a random
URL. We run `git ls-remote $url` to obtain the list of branches and
tags known there, and offer this list in a listbox for the user to
select one or more from.
Like our local branch delete dialog we offer the user a way to filter
their selected branch list down to only those branches that have been
merged into another branch. This is a very common operation as the
user will likely want to select a range of topic branches, but only
delete them if they have been merged into some sort of common trunk.
Unfortunately our remote merge base detection is not nearly as strict
as the local branch version. We only offer remote heads as the test
commit (not any local ones) and we require that all necessary commits
to successfully run git-merge-base are available locally. If one or
more is missing we suggest that the user run a fetch first.
Since the Git remote protocol doesn't let us specify what the tested
commit was when we evaluated our decision to execute the remote delete
there is a race condition here. The user could do a merge test against
the trunk, determine a topic branch was fully merged, but before they
can start pushing the delete request another user could fast-forward
the remote topic branch to a new commit that is not merged into the
trunk. The delete will arrive after, and remove the topic, even though
it was not fully merged.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Git's native command line interface has had branch renaming
support for quite a while, through the -m/-M options to the
git-branch command line tool. This is an extremely useful
feature as users may decide that the name of their current
branch is not an adequate description, or was just entered
incorrectly when it was created.
Even though most people would consider git-branch to be a
Porcelain tool I'm using it here in git-gui as it is the
only code that implements the rather complex set of logic
needed to successfully rename a branch in Git. Currently
that is along the lines of:
*) Backup the ref
*) Backup the reflog
*) Delete the old ref
*) Create the new ref
*) Move the backed up reflog to the new ref
*) Record the rename event in the reflog
*) If the current branch was renamed, update HEAD
*) If HEAD changed, record the rename event in the HEAD reflog
*) Rename the [branch "$name"] section in the config file
Since that is some rather ugly set of functionality to implement
and get right, and some of it isn't easily accessible through the
raw plumbing layer I'm just cheating by relying on the Porcelain.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
The Windows and Mac OS X platforms do not generally use the tearoff
menu feature found on traditional X11 based systems. On Windows the
Tk engine does support the feature, but it really is out of place and
just confuses people who aren't used to working on a UNIX system. On
Mac OS X its not supported for the root menu bar and its submenus, as
it doesn't fit into the overall platform UI model.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
If we cannot locate our git-gui library directory, or we find it
but the tclIndex file is not present there (or it is present but
is not something we are allowed to read) the user cannot use the
application. Rather than silently ignoring the errors related to
the tclIndex file being unavailable we report them up front and
display to the user why we cannot start.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
When we are using our "non-optimized" tclIndex format (which is
just a list of filenames, in the order necessary for source'ing)
we are doing all of our loading before we even tested to see if
GITGUI_VERBOSE was set in the environment. This meant we never
showed the files as we sourced them into the environment.
Now we setup our overloaded auto_load and source scripts before
we attempt to define our library path, or source the scripts that
it mentions. This way GITGUI_VERBOSE is always honored if set.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Because we now try to automatically guess the library directory
in certain installations users may wonder where git-gui is getting
its supporting files from. We now display this location in our
About dialog, and we also include the location we are getting our
Git executables from.
Unfortunately users cannot use this 'About git-gui' dialog to
troubleshoot library loading problems; the dialog is defined by
code that exists in the library directory, creating a catch-22.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
In some workflows it is common for a large number of temporary
branches to be created in a remote repository, get fetched to
clients that typically only use git-gui, and then later have
those branches deleted from the remote repository once they have
been fully merged into all destination branches. Users of git-gui
would obviously like to have their local tracking branches cleaned
up for them, otherwise their local tracking branch namespace would
grow out of control.
The best known way to remove these tracking branches is to run
"git remote prune <remotename>". Even though it is more of a
Porcelain command than plumbing I'm invoking it through the UI,
because frankly I don't see a reason to reimplement its ls-remote
output filtering and config file parsing.
A new configuration option (gui.pruneduringfetch) can be used to
automatically enable running "git remote prune <remotename>" after
the fetch of that remote also completes successfully. This is off
by default as it require an additional network connection and is
not very fast on Cygwin if a large number of tracking branches have
been removed (due to the 2 fork+exec calls per branch).
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Johannes Sixt asked me to try to avoid embedding the runtime location
of git-gui's library directory in the executable script. Not embedding
it helps the MinGW to be relocatable to another directory should a user
wish to install the programs in a directory other than the location the
packager wanted them to be installed into.
Most of this is a hack. We try to determine if the path of our master
git-gui script will be able to locate the lib by ../share/git-gui/lib.
This should be true if $(gitexecdir) and $(libdir) have the same prefix.
If they do then we defer the assignment of $(libdir) until runtime, and
we get it from $argv0 rather than embedding it into the script itself.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Alberto Bertogli reported on #git that git-gui was exiting with
alt-q, while gitk on the same system was exiting with ctrl-q.
That was not what I wanted. I really wanted M1B to be bound to
the Control key on most non-Mac OS X platforms, but according to
Sam Vilain M1 on most systems means alt. Since gitk always does
control, I'm doing the same thing for all non-Mac OS X systems.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Our GITGUI_LIBDIR macro was testing only for @@ at the start of
the path, assuming nobody would ever find that to be a reasonable
prefix for a directory to install our library into. That is most
likely a valid assumption, but its even more unlikely they would
have the start be @@GITGUI_ and the end be @@. Note that we
cannot use the full string here because that would get expanded
by the sed replacement in our Makefile.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Petr Baudis pointed out the main git.git repository's Makefile dies
now if git-gui 0.7.0-rc1 or later is being used and TCL_PATH was not
set to a working tclsh program path. This breaks people who may have
a working build configuration today and suddenly upgrade to the latest
git release.
The tclIndex is required for git-gui to load its associated lib files,
but using the Tcl auto_load procedure to source only the files we need
is a performance optimization. We can emulate the auto_load by just
source'ing every file in that directory, assuming we source class.tcl
first to initialize our crude class system.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This is a simple change to match what gitk does when it shows
a commit; we format using ISO dates (yyyy-mm-dd HH:MM:SS).
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
We can use [list ...] rather than "", especially when we are talking
about values as then they are properly escaped if necessary. Small
nit, but probably not a huge deal as the only data being inlined here
is Tk paths.
Some of the lines in the parser code were longer than 80 characters
wide, and they actually were all the same value on the end part of
the line. Rather than keeping the mess copied-and-pasted around we
can set the last argument into a local variable and reuse it many
times.
The commit display code was also rather difficult to read on an 80
character wide terminal, so I'm moving it all into a double quoted
string that is easier to read.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
If the user doesn't give us a revision parameter to our blame
subcommand then we can generate blame against the working tree
file by passing the file path off to blame with the --contents
argument. In this case we cannot obtain the contents of the
file from the ODB; instead we must obtain the contents by
reading the working directory file as-is.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
The browser subcommand now optionally accepts a single revision
argument; if no revision argument is supplied then we use the
current branch as the tree to browse. This is very common, so
its a nice option.
Our blame subcommand now tries to perform the same assumptions
as the command line git-blame; both the revision and the file
are optional. We assume the argument is a filename if the file
exists in the working directory, otherwise we assume the argument
is a revision name. A -- can be supplied between the two to force
parsing, or before the filename to force it to be a filename.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
I think it was Andy Parkins who pointed out that git gui blame HEAD f
does not work if f is in a subdirectory and we are currently running
git-gui within that subdirectory. This is happening because we did
not take the user's prefix into account when we computed the file
path in the repository.
We now assume the prefix as returned by rev-parse --show-prefix is
valid and we use that during the command line blame subcommand when
we apply the parameters.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Our blame viewer code has historically been a mess simply
because the data for multiple viewers was all crammed into
a single pair of Tcl arrays. This made the code hard to
read and even harder to maintain.
Now that we have a slightly better way of tracking the data
for our "meta-widgets" we can make use of it here in the
blame viewer to cleanup the code and make it easier to work
with long term.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
If a variable reference to a field is to an array, and it is
the only reference to that field in that method we cannot make
it an inlined [set foo] call as the regexp was converting the
Tcl code wrong. We were producing "[set foo](x)" for "$foo(x)",
and that isn't valid Tcl when foo is an array. So we just punt
if the only occurance has a ( after it.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Now that we have a slightly easier method of working with per-widget
data we should make use of that technique in our browser and console
meta-widgets, as both have a decent amount of information that they
store on a per-widget basis and our current approach of handling
it is difficult to follow.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
As most of the git-gui interface is based upon "meta-widgets"
that need to carry around a good deal of state (e.g. console
windows, browser windows, blame viewer) we have a good deal
of messy code that tries to store this meta-widget state in
global arrays, where keys into the array are formed from a
union of a unique "object instance id" and the field name.
This is a simple class system for Tcl that allows us to
hide much of that mess by making Tcl do what it does best;
process strings to manipulate its own code during startup.
Each object instance is placed into its own namespace. The
namespace is created when the object instance is created and
the namespace is destroyed when the object instance is removed
from the system. Within that namespace we place variables for
each field within the class; these variables can themselves be
scalar values or full-blown Tcl arrays.
A simple class might be defined as:
class map {
field data
field size 0
constructor {} {
return $this
}
method set {name value} {
set data($name) $value
incr size
}
method size {} {
return $size
} ifdeleted { return 0 }
}
All fields must be declared before any constructors or methods. This
allows our class to generate a list of the fields so it can properly
alter the definition of the constructor and method bodies prior to
passing them off to Tcl for definition with proc. A field may optionally
be given a default/initial value. This can only be done for non-array
type fields.
Constructors are given full access to all fields of the class, so they
can initialize the data values. The default values of fields (if any)
are set before the constructor runs, and the implicit local variable
$this is initialized to the instance identifier.
Methods are given access to fields they actually use in their body.
Every method has an implicit "this" argument inserted as its first
parameter; callers of methods must be sure they supply this value.
Some basic optimization tricks are performed (but not much). We
try to only upvar (locally bind) fields that are accessed within a
method, but we err on the side of caution and may upvar more than
we need to. If a variable is accessed only once within a method
and that access is by $foo (read) we avoid the upvar and instead
use [set foo] to obtain the value. This is slightly faster as Tcl
does not need to lookup the variable twice.
We also offer some small syntatic sugar for interacting with Tk and
the fileevent callback system in Tcl. If a field (say "foo") is used
as "@foo" we insert instead the true global variable name of that
variable into the body of the constructor or method. This allows easy
binding to Tk textvariable options, e.g.:
label $w.title -textvariable @title
Proper namespace callbacks can also be setup with the special cb proc
that is defined in each namespace. [cb _foo a] will invoke the method
_foo in the current namespace, passing it $this as the first (implied)
parameter and a as the second parameter. This makes it very simple to
connect an object instance to a -command option for a Tk widget or to
a fileevent readable or writable for a file channel.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
I found it useful to be able to use j/k (vi-like keys) to move
up and down the list of branches to merge and shift-j/k to do
the selection, much as shift-up/down (arrow keys) would alter
the selection.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
All menu entries talk about "staging" and "unstaging" changes, but the
titles of the file lists use different wording, which may confuse
newcomers.
Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Since we support vi-like keys for scrolling in other UI contexts
we can easily do so here too. Tk's handy little `event generate'
makes this a lot easier than I thought it would be. We may want
to go back and fix some of the other vi-like bindings to redirect
to the arrow and pageup/pagedown keys, rather than running the
view changes directly.
I've bound 'v' to visualize, as this is a somewhat common thing
to want to do in the merge dialog. Control (or Command) Return
is also bound to start the merge, much as it is bound in the
main window to activate the commit.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
When merging branches using our local merge feature it can be
handy to know the first few digits of the commit the ref points
at as well as the short description of the branch name.
Unfortunately I'm unable to use three listboxes in a row, as Tcl
freaks out and refuses to let me have a selection in more than
one of them at any given point in time. So instead we use a
fixed width font in the existing listbox and organize the data
into three columns. Not nearly as nice looking, but users can
continue to use the listbox's features.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Johannes Sixt pointed out that git-gui was randomly selecting
which branch (or tag!) it will show in the merge dialog when
more than one ref points at the same commit. This can be a
problem for the user if they want to merge a branch, but the
ref that git-gui selected to display was actually a tag that
points at the commit at the tip of that branch. Since the
user is looking for the branch, and not the tag, its confusing
to not find it, and worse, merging the tag causes git-merge to
generate a different message than if the branch was selected.
While I am in here and am messing around I have changed the
for-each-ref usage to take advantage of its --tcl formatting,
and to fetch the subject line of the commit (or tag) we are
looking at. This way we could present the subject line in the
UI to the user, given them an even better chance to select
the correct branch.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Like the console procs I have moved the code related to merge
support into their own namespace, so that they are isolated
from the rest of the world.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Users who are used to vi and recent versions of gitk may want
to scroll the diff region using vi style keybindings. Since
these aren't bound to anything else and that widget does not
accept focus for data input, we can easily support that too.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
To help modularize git-gui better I'm isolating the code and
variables required to handle our little console windows into
their own namespace. This way we can say console::new rather
than new_console, and the hidden internal procs to create the
window and read data from our filehandle are off in their own
private little land, where most users don't see them.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
I'm finding it difficult to work with a 6,000+ line Tcl script
and not go insane while looking for a particular block of code.
Since most of the program is organized into different units of
functionality and not all users will need all units immediately
on startup we can improve things by splitting procs out into
multiple files and let auto_load handle things for us.
This should help not only to better organize the source, but
it may also improve startup times for some users as the Tcl
parser does not need to read as much script before it can show
the UI. In many cases the user can avoid reading at least half
of git-gui now.
Unfortunately we now need a library directory in our runtime
location. This is currently assumed to be $(sharedir)/git-gui/lib
and its expected that the Makefile invoker will setup some sort of
reasonable sharedir value for us, or let us assume its going to be
$(gitexecdir)/../share.
We now also require a tclsh (in TCL_PATH) to just run the Makefile,
as we use tclsh to generate the tclIndex for our lib directory. I'm
hoping this is not an unncessary burden on end-users who are building
from source.
I haven't really made any functionality changes here, this is just a
huge migration of code from one file to many smaller files. All of
the new changes are to setup the library path and install the library
files.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Like core-Git we now track the values that we embed into our shell
script wrapper, and we "recompile" that wrapper if they are changed.
This concept was lifted from git.git's Makefile, where a similar
thing was done by Eygene Ryabinkin. Too bad it wasn't just done
here in git-gui from the beginning, as the git.git Makefile support
for GIT-GUI-VARS was really just because git-gui doesn't do it on
its own.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Whenever we want to execute a git subcommand from the plumbing
layer (and on rare occasion, the more porcelain-ish layer) we
tend to use our proc wrapper, just to make the code slightly
cleaner at the call sites. I wasn't doing that in a couple of
places, so this is a simple cleanup to correct that.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Rather than passing "-font font_ui" to every widget that we
create we can instead reconfigure the option database for
all widget classes to use our font_ui as the default widget
font. This way Tk will automatically setup their defaults
for us, and we can reduce the size of the application.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
An earlier change tossed these optionMenu font configurations
all over the code, when really we can just rename the proc to
a hidden internal name and provide our own wrapper to install
the font configuration we really want.
We also don't need to set these option database entries in all
of the procedures that open dialogs; instead we should just set
one time, them after we have the font configuration ready for use.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Since Tk automatically wraps lines for us in tk_messageBox
widgets we don't need to try to wrap them ourselves. Its
actually worse that we linewrapped this here in the script,
as not all fonts will render this dialog nicely.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
A coworker who was new to git-gui recently tried to make an octopus
merge when he did not quite mean to. Unfortunately in his case the
branches had file level conflicts and failed to merge with the octopus
strategy, and he didn't quite know why this happened. Since most users
really don't want to perform an octopus merge this additional safety
valve in front of the merge process is a good thing.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Now that the command line git-commit has made displaying
the subject (first line) of the newly created commit popular
we can easily do the same thing here in git-gui, without the
ugly part of forking off a child process to obtain that first
line.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>