mirror of
https://github.com/git/git.git
synced 2026-03-13 10:23:30 +01:00
Merge branch 'master' of git://repo.or.cz/alt-git
This commit is contained in:
@@ -37,9 +37,6 @@ man7dir=$(mandir)/man7
|
||||
|
||||
ASCIIDOC=asciidoc
|
||||
ASCIIDOC_EXTRA =
|
||||
ifdef ASCIIDOC8
|
||||
ASCIIDOC_EXTRA += -a asciidoc7compatible
|
||||
endif
|
||||
INSTALL?=install
|
||||
RM ?= rm -f
|
||||
DOC_REF = origin/man
|
||||
@@ -52,6 +49,13 @@ DOCBOOK2X_TEXI=docbook2x-texi
|
||||
-include ../config.mak.autogen
|
||||
-include ../config.mak
|
||||
|
||||
ifdef ASCIIDOC8
|
||||
ASCIIDOC_EXTRA += -a asciidoc7compatible
|
||||
endif
|
||||
ifdef DOCBOOK_XSL_172
|
||||
ASCIIDOC_EXTRA += -a docbook-xsl-172
|
||||
endif
|
||||
|
||||
#
|
||||
# Please note that there is a minor bug in asciidoc.
|
||||
# The version after 6.0.3 _will_ include the patch found here:
|
||||
|
||||
@@ -23,7 +23,9 @@ ifdef::backend-docbook[]
|
||||
endif::backend-docbook[]
|
||||
|
||||
ifdef::backend-docbook[]
|
||||
ifndef::docbook-xsl-172[]
|
||||
# "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
|
||||
# v1.72 breaks with this because it replaces dots not in roff requests.
|
||||
[listingblock]
|
||||
<example><title>{title}</title>
|
||||
<literallayout>
|
||||
@@ -36,6 +38,7 @@ ifdef::doctype-manpage[]
|
||||
endif::doctype-manpage[]
|
||||
</literallayout>
|
||||
{title#}</example>
|
||||
endif::docbook-xsl-172[]
|
||||
endif::backend-docbook[]
|
||||
|
||||
ifdef::doctype-manpage[]
|
||||
|
||||
@@ -432,6 +432,12 @@ fetch.unpackLimit::
|
||||
pack from a push can make the push operation complete faster,
|
||||
especially on slow filesystems.
|
||||
|
||||
format.numbered::
|
||||
A boolean which can enable sequence numbers in patch subjects.
|
||||
Seting this option to "auto" will enable it only if there is
|
||||
more than one patch. See --numbered option in
|
||||
gitlink:git-format-patch[1].
|
||||
|
||||
format.headers::
|
||||
Additional email headers to include in a patch to be submitted
|
||||
by mail. See gitlink:git-format-patch[1].
|
||||
|
||||
@@ -1090,11 +1090,6 @@ server like git Native transport does. Any stock HTTP server
|
||||
that does not even support directory index would suffice. But
|
||||
you must prepare your repository with `git-update-server-info`
|
||||
to help dumb transport downloaders.
|
||||
+
|
||||
There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload`
|
||||
programs, which are 'commit walkers'; they outlived their
|
||||
usefulness when git Native and SSH transports were introduced,
|
||||
and are not used by `git pull` or `git push` scripts.
|
||||
|
||||
Once you fetch from the remote repository, you `merge` that
|
||||
with your current branch.
|
||||
@@ -1157,7 +1152,7 @@ back to the earlier repository with "hello" and "example" file,
|
||||
and bring ourselves back to the pre-merge state:
|
||||
|
||||
------------
|
||||
$ git show-branch --more=3 master mybranch
|
||||
$ git show-branch --more=2 master mybranch
|
||||
! [master] Merge work in mybranch
|
||||
* [mybranch] Merge work in mybranch
|
||||
--
|
||||
@@ -1220,7 +1215,7 @@ $ git-read-tree -m -u $mb HEAD mybranch
|
||||
This is the same `git-read-tree` command we have already seen,
|
||||
but it takes three trees, unlike previous examples. This reads
|
||||
the contents of each tree into different 'stage' in the index
|
||||
file (the first tree goes to stage 1, the second stage 2,
|
||||
file (the first tree goes to stage 1, the second to stage 2,
|
||||
etc.). After reading three trees into three stages, the paths
|
||||
that are the same in all three stages are 'collapsed' into stage
|
||||
0. Also paths that are the same in two of three stages are
|
||||
|
||||
@@ -224,6 +224,7 @@ See Also
|
||||
--------
|
||||
gitlink:git-status[1]
|
||||
gitlink:git-rm[1]
|
||||
gitlink:git-reset[1]
|
||||
gitlink:git-mv[1]
|
||||
gitlink:git-commit[1]
|
||||
gitlink:git-update-index[1]
|
||||
|
||||
@@ -105,7 +105,7 @@ OPTIONS
|
||||
'--track' were given.
|
||||
|
||||
--no-track::
|
||||
When -b is given and a branch is created off a remote branch,
|
||||
When a branch is created off a remote branch,
|
||||
set up configuration so that git-pull will not retrieve data
|
||||
from the remote branch, ignoring the branch.autosetupmerge
|
||||
configuration variable.
|
||||
|
||||
@@ -154,10 +154,13 @@ EXAMPLES
|
||||
--------
|
||||
When recording your own work, the contents of modified files in
|
||||
your working tree are temporarily stored to a staging area
|
||||
called the "index" with gitlink:git-add[1]. Removal
|
||||
of a file is staged with gitlink:git-rm[1]. After building the
|
||||
state to be committed incrementally with these commands, `git
|
||||
commit` (without any pathname parameter) is used to record what
|
||||
called the "index" with gitlink:git-add[1]. A file can be
|
||||
reverted back, only in the index but not in the working tree,
|
||||
to that of the last commit with `git-reset HEAD -- <file>`,
|
||||
which effectively reverts `git-add` and prevents the changes to
|
||||
this file from participating in the next commit. After building
|
||||
the state to be committed incrementally with these commands,
|
||||
`git commit` (without any pathname parameter) is used to record what
|
||||
has been staged so far. This is the most basic form of the
|
||||
command. An example:
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ git-cvsexportcommit - Export a single commit to a CVS checkout
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
|
||||
'git-cvsexportcommit' [-h] [-u] [-v] [-c] [-P] [-p] [-a] [-d cvsroot] [-w cvsworkdir] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
@@ -16,8 +16,9 @@ DESCRIPTION
|
||||
Exports a commit from GIT to a CVS checkout, making it easier
|
||||
to merge patches from a git repository into a CVS repository.
|
||||
|
||||
Execute it from the root of the CVS working copy. GIT_DIR must be defined.
|
||||
See examples below.
|
||||
Specify the name of a CVS checkout using the -w switch or execute it
|
||||
from the root of the CVS working copy. In the latter case GIT_DIR must
|
||||
be defined. See examples below.
|
||||
|
||||
It does its best to do the safe thing, it will check that the files are
|
||||
unchanged and up to date in the CVS checkout, and it will not autocommit
|
||||
@@ -61,6 +62,11 @@ OPTIONS
|
||||
-u::
|
||||
Update affected files from CVS repository before attempting export.
|
||||
|
||||
-w::
|
||||
Specify the location of the CVS checkout to use for the export. This
|
||||
option does not require GIT_DIR to be set before execution if the
|
||||
current directory is within a git repository.
|
||||
|
||||
-v::
|
||||
Verbose.
|
||||
|
||||
@@ -76,6 +82,12 @@ $ git-cvsexportcommit -v <commit-sha1>
|
||||
$ cvs commit -F .msg <files>
|
||||
------------
|
||||
|
||||
Merge one patch into CVS (-c and -w options). The working directory is within the Git Repo::
|
||||
+
|
||||
------------
|
||||
$ git-cvsexportcommit -v -c -w ~/project_cvs_checkout <commit-sha1>
|
||||
------------
|
||||
|
||||
Merge pending patches into CVS automatically -- only if you really know what you are doing::
|
||||
+
|
||||
------------
|
||||
@@ -86,11 +98,11 @@ $ git-cherry cvshead myhead | sed -n 's/^+ //p' | xargs -l1 git-cvsexportcommit
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Martin Langhoff <martin@catalyst.net.nz>
|
||||
Written by Martin Langhoff <martin@catalyst.net.nz> and others.
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
Documentation by Martin Langhoff <martin@catalyst.net.nz>
|
||||
Documentation by Martin Langhoff <martin@catalyst.net.nz> and others.
|
||||
|
||||
GIT
|
||||
---
|
||||
|
||||
@@ -9,9 +9,10 @@ git-format-patch - Prepare patches for e-mail submission
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--thread]
|
||||
'git-format-patch' [-k] [-o <dir> | --stdout] [--thread]
|
||||
[--attach[=<boundary>] | --inline[=<boundary>]]
|
||||
[-s | --signoff] [<common diff options>]
|
||||
[-n | --numbered | -N | --no-numbered]
|
||||
[--start-number <n>] [--numbered-files]
|
||||
[--in-reply-to=Message-Id] [--suffix=.<sfx>]
|
||||
[--ignore-if-in-upstream]
|
||||
@@ -77,6 +78,9 @@ include::diff-options.txt[]
|
||||
-n|--numbered::
|
||||
Name output in '[PATCH n/m]' format.
|
||||
|
||||
-N|--no-numbered::
|
||||
Name output in '[PATCH]' format.
|
||||
|
||||
--start-number <n>::
|
||||
Start numbering the patches at <n> instead of 1.
|
||||
|
||||
@@ -142,15 +146,16 @@ not add any suffix.
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
You can specify extra mail header lines to be added to each
|
||||
message in the repository configuration. You can also specify
|
||||
new defaults for the subject prefix and file suffix.
|
||||
You can specify extra mail header lines to be added to each message
|
||||
in the repository configuration, new defaults for the subject prefix
|
||||
and file suffix, and number patches when outputting more than one.
|
||||
|
||||
------------
|
||||
[format]
|
||||
headers = "Organization: git-foo\n"
|
||||
subjectprefix = CHANGE
|
||||
suffix = .txt
|
||||
numbered = auto
|
||||
------------
|
||||
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ SYNOPSIS
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Acts as a filter, extracting the commit ID stored in archives created by
|
||||
git-tar-tree. It reads only the first 1024 bytes of input, thus its
|
||||
gitlink:git-archive[1]. It reads only the first 1024 bytes of input, thus its
|
||||
runtime is not influenced by the size of <tarfile> very much.
|
||||
|
||||
If no commit ID is found, git-get-tar-commit-id quietly exists with a
|
||||
return code of 1. This can happen if <tarfile> had not been created
|
||||
using git-tar-tree or if the first parameter of git-tar-tree had been
|
||||
using git-archive or if the <treeish> parameter of git-archive had been
|
||||
a tree ID instead of a commit ID or tag.
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-push' [--all] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>]
|
||||
[--repo=all] [-f | --force] [-v] [<repository> <refspec>...]
|
||||
[--repo=all] [-f | --force] [-v | --verbose] [<repository> <refspec>...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -95,7 +95,7 @@ the remote repository.
|
||||
transfer spends extra cycles to minimize the number of
|
||||
objects to be sent and meant to be used on slower connection.
|
||||
|
||||
-v::
|
||||
-v, \--verbose::
|
||||
Run verbosely.
|
||||
|
||||
include::urls-remotes.txt[]
|
||||
|
||||
@@ -79,7 +79,7 @@ caution.
|
||||
Fetch updates for a named set of remotes in the repository as defined by
|
||||
remotes.<group>. If a named group is not specified on the command line,
|
||||
the configuration parameter remotes.default will get used; if
|
||||
remotes.default is not defined, all remotes which do not the
|
||||
remotes.default is not defined, all remotes which do not have the
|
||||
configuration parameter remote.<name>.skipDefaultUpdate set to true will
|
||||
be updated. (See gitlink:git-config[1]).
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ SYNOPSIS
|
||||
[ \--not ]
|
||||
[ \--all ]
|
||||
[ \--stdin ]
|
||||
[ \--quiet ]
|
||||
[ \--topo-order ]
|
||||
[ \--parents ]
|
||||
[ \--timestamp ]
|
||||
@@ -270,6 +271,14 @@ limiting may be applied.
|
||||
In addition to the '<commit>' listed on the command
|
||||
line, read them from the standard input.
|
||||
|
||||
--quiet::
|
||||
|
||||
Don't print anything to standard output. This form of
|
||||
git-rev-list is primarly meant to allow the caller to
|
||||
test the exit status to see if a range of objects is fully
|
||||
connected (or not). It is faster than redirecting stdout
|
||||
to /dev/null as the output does not have to be formatted.
|
||||
|
||||
--cherry-pick::
|
||||
|
||||
Omit any commit that introduces the same change as
|
||||
|
||||
@@ -148,22 +148,23 @@ with `$Id$` upon check-in.
|
||||
`filter`
|
||||
^^^^^^^^
|
||||
|
||||
A `filter` attribute can be set to a string value. This names
|
||||
A `filter` attribute can be set to a string value that names a
|
||||
filter driver specified in the configuration.
|
||||
|
||||
A filter driver consists of `clean` command and `smudge`
|
||||
A filter driver consists of a `clean` command and a `smudge`
|
||||
command, either of which can be left unspecified. Upon
|
||||
checkout, when `smudge` command is specified, the command is fed
|
||||
the blob object from its standard input, and its standard output
|
||||
is used to update the worktree file. Similarly, `clean` command
|
||||
is used to convert the contents of worktree file upon checkin.
|
||||
checkout, when the `smudge` command is specified, the command is
|
||||
fed the blob object from its standard input, and its standard
|
||||
output is used to update the worktree file. Similarly, the
|
||||
`clean` command is used to convert the contents of worktree file
|
||||
upon checkin.
|
||||
|
||||
Missing filter driver definition in the config is not an error
|
||||
A missing filter driver definition in the config is not an error
|
||||
but makes the filter a no-op passthru.
|
||||
|
||||
The content filtering is done to massage the content into a
|
||||
shape that is more convenient for the platform, filesystem, and
|
||||
the user to use. The keyword here is "more convenient" and not
|
||||
the user to use. The key phrase here is "more convenient" and not
|
||||
"turning something unusable into usable". In other words, the
|
||||
intent is that if someone unsets the filter driver definition,
|
||||
or does not have the appropriate filter program, the project
|
||||
|
||||
@@ -475,7 +475,7 @@ Bisecting: 3537 revisions left to test after this
|
||||
If you run "git branch" at this point, you'll see that git has
|
||||
temporarily moved you to a new branch named "bisect". This branch
|
||||
points to a commit (with commit id 65934...) that is reachable from
|
||||
v2.6.19 but not from v2.6.18. Compile and test it, and see whether
|
||||
"master" but not from v2.6.18. Compile and test it, and see whether
|
||||
it crashes. Assume it does crash. Then:
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -1367,7 +1367,7 @@ If you make a commit that you later wish you hadn't, there are two
|
||||
fundamentally different ways to fix the problem:
|
||||
|
||||
1. You can create a new commit that undoes whatever was done
|
||||
by the previous commit. This is the correct thing if your
|
||||
by the old commit. This is the correct thing if your
|
||||
mistake has already been made public.
|
||||
|
||||
2. You can go back and modify the old commit. You should
|
||||
@@ -1568,7 +1568,7 @@ $ git log master@{1}
|
||||
-------------------------------------------------
|
||||
|
||||
This lists the commits reachable from the previous version of the head.
|
||||
This syntax can be used to with any git command that accepts a commit,
|
||||
This syntax can be used with any git command that accepts a commit,
|
||||
not just with git log. Some other examples:
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
4
Makefile
4
Makefile
@@ -115,6 +115,8 @@ all::
|
||||
#
|
||||
# Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
|
||||
#
|
||||
# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
|
||||
#
|
||||
# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
|
||||
# MakeMaker (e.g. using ActiveState under Cygwin).
|
||||
#
|
||||
@@ -1029,6 +1031,8 @@ test-date$X: date.o ctype.o
|
||||
|
||||
test-delta$X: diff-delta.o patch-delta.o
|
||||
|
||||
test-parse-options$X: parse-options.o
|
||||
|
||||
.PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
|
||||
|
||||
test-%$X: test-%.o $(GITLIBS)
|
||||
|
||||
@@ -21,7 +21,6 @@ static const char * const builtin_add_usage[] = {
|
||||
};
|
||||
|
||||
static int take_worktree_changes;
|
||||
static const char *excludes_file;
|
||||
|
||||
static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
|
||||
{
|
||||
@@ -61,12 +60,7 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec,
|
||||
memset(dir, 0, sizeof(*dir));
|
||||
if (!ignored_too) {
|
||||
dir->collect_ignored = 1;
|
||||
dir->exclude_per_dir = ".gitignore";
|
||||
path = git_path("info/exclude");
|
||||
if (!access(path, R_OK))
|
||||
add_excludes_from_file(dir, path);
|
||||
if (excludes_file != NULL && !access(excludes_file, R_OK))
|
||||
add_excludes_from_file(dir, excludes_file);
|
||||
setup_standard_excludes(dir);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -120,7 +114,7 @@ void add_files_to_cache(int verbose, const char *prefix, const char **files)
|
||||
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
rev.diffopt.format_callback = update_callback;
|
||||
rev.diffopt.format_callback_data = &verbose;
|
||||
run_diff_files(&rev, 0);
|
||||
run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
|
||||
}
|
||||
|
||||
static void refresh(int verbose, const char **pathspec)
|
||||
@@ -141,18 +135,6 @@ static void refresh(int verbose, const char **pathspec)
|
||||
free(seen);
|
||||
}
|
||||
|
||||
static int git_add_config(const char *var, const char *value)
|
||||
{
|
||||
if (!strcmp(var, "core.excludesfile")) {
|
||||
if (!value)
|
||||
die("core.excludesfile without value");
|
||||
excludes_file = xstrdup(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
int interactive_add(void)
|
||||
{
|
||||
const char *argv[2] = { "add--interactive", NULL };
|
||||
@@ -193,7 +175,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
exit(interactive_add());
|
||||
}
|
||||
|
||||
git_config(git_add_config);
|
||||
git_config(git_default_config);
|
||||
|
||||
newfd = hold_locked_index(&lock_file, 1);
|
||||
|
||||
|
||||
@@ -1993,7 +1993,7 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
return ce_match_stat(ce, st, 1);
|
||||
return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID);
|
||||
}
|
||||
|
||||
static int check_patch(struct patch *patch, struct patch *prev_patch)
|
||||
|
||||
@@ -435,9 +435,7 @@ static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_resu
|
||||
cp++;
|
||||
if (!*cp)
|
||||
break;
|
||||
np = strchr(cp, '\n');
|
||||
if (!np)
|
||||
np = cp + strlen(cp);
|
||||
np = strchrnul(cp, '\n');
|
||||
if (pass) {
|
||||
lrr_list[i].line = cp;
|
||||
lrr_list[i].name = cp + 41;
|
||||
@@ -461,9 +459,7 @@ static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_resu
|
||||
rref++;
|
||||
if (!*rref)
|
||||
break;
|
||||
next = strchr(rref, '\n');
|
||||
if (!next)
|
||||
next = rref + strlen(rref);
|
||||
next = strchrnul(rref, '\n');
|
||||
rreflen = next - rref;
|
||||
|
||||
for (i = 0; i < lrr_count; i++) {
|
||||
|
||||
104
builtin-fetch.c
104
builtin-fetch.c
@@ -8,10 +8,12 @@
|
||||
#include "path-list.h"
|
||||
#include "remote.h"
|
||||
#include "transport.h"
|
||||
#include "run-command.h"
|
||||
|
||||
static const char fetch_usage[] = "git-fetch [-a | --append] [--upload-pack <upload-pack>] [-f | --force] [--no-tags] [-t | --tags] [-k | --keep] [-u | --update-head-ok] [--depth <depth>] [-v | --verbose] [<repository> <refspec>...]";
|
||||
|
||||
static int append, force, tags, no_tags, update_head_ok, verbose, quiet;
|
||||
static const char *depth;
|
||||
static char *default_rla = NULL;
|
||||
static struct transport *transport;
|
||||
|
||||
@@ -152,6 +154,7 @@ static int s_update_ref(const char *action,
|
||||
}
|
||||
|
||||
#define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
|
||||
#define REFCOL_WIDTH 10
|
||||
|
||||
static int update_local_ref(struct ref *ref,
|
||||
const char *remote,
|
||||
@@ -181,8 +184,9 @@ static int update_local_ref(struct ref *ref,
|
||||
|
||||
if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
|
||||
if (verbose)
|
||||
sprintf(display, "= %-*s %s -> %s", SUMMARY_WIDTH,
|
||||
"[up to date]", remote, pretty_ref);
|
||||
sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH,
|
||||
"[up to date]", REFCOL_WIDTH, remote,
|
||||
pretty_ref);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -194,15 +198,17 @@ static int update_local_ref(struct ref *ref,
|
||||
* If this is the head, and it's not okay to update
|
||||
* the head, and the old value of the head isn't empty...
|
||||
*/
|
||||
sprintf(display, "! %-*s %s -> %s (can't fetch in current branch)",
|
||||
SUMMARY_WIDTH, "[rejected]", remote, pretty_ref);
|
||||
sprintf(display, "! %-*s %-*s -> %s (can't fetch in current branch)",
|
||||
SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
|
||||
pretty_ref);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!is_null_sha1(ref->old_sha1) &&
|
||||
!prefixcmp(ref->name, "refs/tags/")) {
|
||||
sprintf(display, "- %-*s %s -> %s",
|
||||
SUMMARY_WIDTH, "[tag update]", remote, pretty_ref);
|
||||
sprintf(display, "- %-*s %-*s -> %s",
|
||||
SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote,
|
||||
pretty_ref);
|
||||
return s_update_ref("updating tag", ref, 0);
|
||||
}
|
||||
|
||||
@@ -220,8 +226,8 @@ static int update_local_ref(struct ref *ref,
|
||||
what = "[new branch]";
|
||||
}
|
||||
|
||||
sprintf(display, "* %-*s %s -> %s",
|
||||
SUMMARY_WIDTH, what, remote, pretty_ref);
|
||||
sprintf(display, "* %-*s %-*s -> %s", SUMMARY_WIDTH, what,
|
||||
REFCOL_WIDTH, remote, pretty_ref);
|
||||
return s_update_ref(msg, ref, 0);
|
||||
}
|
||||
|
||||
@@ -230,20 +236,21 @@ static int update_local_ref(struct ref *ref,
|
||||
strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
|
||||
strcat(quickref, "..");
|
||||
strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
|
||||
sprintf(display, " %-*s %s -> %s (fast forward)",
|
||||
SUMMARY_WIDTH, quickref, remote, pretty_ref);
|
||||
sprintf(display, " %-*s %-*s -> %s", SUMMARY_WIDTH, quickref,
|
||||
REFCOL_WIDTH, remote, pretty_ref);
|
||||
return s_update_ref("fast forward", ref, 1);
|
||||
} else if (force || ref->force) {
|
||||
char quickref[84];
|
||||
strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
|
||||
strcat(quickref, "...");
|
||||
strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
|
||||
sprintf(display, "+ %-*s %s -> %s (forced update)",
|
||||
SUMMARY_WIDTH, quickref, remote, pretty_ref);
|
||||
sprintf(display, "+ %-*s %-*s -> %s (forced update)",
|
||||
SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote, pretty_ref);
|
||||
return s_update_ref("forced-update", ref, 1);
|
||||
} else {
|
||||
sprintf(display, "! %-*s %s -> %s (non fast forward)",
|
||||
SUMMARY_WIDTH, "[rejected]", remote, pretty_ref);
|
||||
sprintf(display, "! %-*s %-*s -> %s (non fast forward)",
|
||||
SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
|
||||
pretty_ref);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -330,9 +337,72 @@ static void store_updated_refs(const char *url, struct ref *ref_map)
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* We would want to bypass the object transfer altogether if
|
||||
* everything we are going to fetch already exists and connected
|
||||
* locally.
|
||||
*
|
||||
* The refs we are going to fetch are in to_fetch (nr_heads in
|
||||
* total). If running
|
||||
*
|
||||
* $ git-rev-list --objects to_fetch[0] to_fetch[1] ... --not --all
|
||||
*
|
||||
* does not error out, that means everything reachable from the
|
||||
* refs we are going to fetch exists and is connected to some of
|
||||
* our existing refs.
|
||||
*/
|
||||
static int quickfetch(struct ref *ref_map)
|
||||
{
|
||||
struct child_process revlist;
|
||||
struct ref *ref;
|
||||
char **argv;
|
||||
int i, err;
|
||||
|
||||
/*
|
||||
* If we are deepening a shallow clone we already have these
|
||||
* objects reachable. Running rev-list here will return with
|
||||
* a good (0) exit status and we'll bypass the fetch that we
|
||||
* really need to perform. Claiming failure now will ensure
|
||||
* we perform the network exchange to deepen our history.
|
||||
*/
|
||||
if (depth)
|
||||
return -1;
|
||||
|
||||
for (i = 0, ref = ref_map; ref; ref = ref->next)
|
||||
i++;
|
||||
if (!i)
|
||||
return 0;
|
||||
|
||||
argv = xmalloc(sizeof(*argv) * (i + 6));
|
||||
i = 0;
|
||||
argv[i++] = xstrdup("rev-list");
|
||||
argv[i++] = xstrdup("--quiet");
|
||||
argv[i++] = xstrdup("--objects");
|
||||
for (ref = ref_map; ref; ref = ref->next)
|
||||
argv[i++] = xstrdup(sha1_to_hex(ref->old_sha1));
|
||||
argv[i++] = xstrdup("--not");
|
||||
argv[i++] = xstrdup("--all");
|
||||
argv[i++] = NULL;
|
||||
|
||||
memset(&revlist, 0, sizeof(revlist));
|
||||
revlist.argv = (const char**)argv;
|
||||
revlist.git_cmd = 1;
|
||||
revlist.no_stdin = 1;
|
||||
revlist.no_stdout = 1;
|
||||
revlist.no_stderr = 1;
|
||||
err = run_command(&revlist);
|
||||
|
||||
for (i = 0; argv[i]; i++)
|
||||
free(argv[i]);
|
||||
free(argv);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int fetch_refs(struct transport *transport, struct ref *ref_map)
|
||||
{
|
||||
int ret = transport_fetch_refs(transport, ref_map);
|
||||
int ret = quickfetch(ref_map);
|
||||
if (ret)
|
||||
ret = transport_fetch_refs(transport, ref_map);
|
||||
if (!ret)
|
||||
store_updated_refs(transport->url, ref_map);
|
||||
transport_unlock_pack(transport);
|
||||
@@ -384,7 +454,7 @@ static struct ref *find_non_local_tags(struct transport *transport,
|
||||
|
||||
if (!path_list_has_path(&existing_refs, ref_name) &&
|
||||
!path_list_has_path(&new_refs, ref_name) &&
|
||||
lookup_object(ref->old_sha1)) {
|
||||
has_sha1_file(ref->old_sha1)) {
|
||||
path_list_insert(ref_name, &new_refs);
|
||||
|
||||
rm = alloc_ref(strlen(ref_name) + 1);
|
||||
@@ -468,7 +538,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
||||
static const char **refs = NULL;
|
||||
int ref_nr = 0;
|
||||
int cmd_len = 0;
|
||||
const char *depth = NULL, *upload_pack = NULL;
|
||||
const char *upload_pack = NULL;
|
||||
int keep = 0;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
|
||||
@@ -304,7 +304,7 @@ static const char *find_wholine(const char *who, int wholen, const char *buf, un
|
||||
if (!eol)
|
||||
return "";
|
||||
eol++;
|
||||
if (eol[1] == '\n')
|
||||
if (*eol == '\n')
|
||||
return ""; /* end of header */
|
||||
buf = eol;
|
||||
}
|
||||
@@ -847,7 +847,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
|
||||
OPT_GROUP(""),
|
||||
OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"),
|
||||
OPT_STRING( 0 , "format", &format, "format", "format to use for the output"),
|
||||
OPT_CALLBACK(0 , "sort", &sort_tail, "key",
|
||||
OPT_CALLBACK(0 , "sort", sort_tail, "key",
|
||||
"field name to sort on", &opt_parse_sort),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
@@ -273,6 +273,8 @@ static int istitlechar(char c)
|
||||
static char *extra_headers = NULL;
|
||||
static int extra_headers_size = 0;
|
||||
static const char *fmt_patch_suffix = ".patch";
|
||||
static int numbered = 0;
|
||||
static int auto_number = 0;
|
||||
|
||||
static int git_format_config(const char *var, const char *value)
|
||||
{
|
||||
@@ -297,6 +299,15 @@ static int git_format_config(const char *var, const char *value)
|
||||
if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(var, "format.numbered")) {
|
||||
if (!strcasecmp(value, "auto")) {
|
||||
auto_number = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
numbered = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return git_log_config(var, value);
|
||||
}
|
||||
@@ -466,7 +477,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
struct rev_info rev;
|
||||
int nr = 0, total, i, j;
|
||||
int use_stdout = 0;
|
||||
int numbered = 0;
|
||||
int start_number = -1;
|
||||
int keep_subject = 0;
|
||||
int numbered_files = 0; /* _just_ numbers */
|
||||
@@ -503,6 +513,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
else if (!strcmp(argv[i], "-n") ||
|
||||
!strcmp(argv[i], "--numbered"))
|
||||
numbered = 1;
|
||||
else if (!strcmp(argv[i], "-N") ||
|
||||
!strcmp(argv[i], "--no-numbered")) {
|
||||
numbered = 0;
|
||||
auto_number = 0;
|
||||
}
|
||||
else if (!prefixcmp(argv[i], "--start-number="))
|
||||
start_number = strtol(argv[i] + 15, NULL, 10);
|
||||
else if (!strcmp(argv[i], "--numbered-files"))
|
||||
@@ -642,6 +657,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
list[nr - 1] = commit;
|
||||
}
|
||||
total = nr;
|
||||
if (!keep_subject && auto_number && total > 1)
|
||||
numbered = 1;
|
||||
if (numbered)
|
||||
rev.total = total + start_number - 1;
|
||||
rev.add_signoff = add_signoff;
|
||||
|
||||
@@ -115,6 +115,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||
flags |= TRANSPORT_PUSH_FORCE;
|
||||
if (dry_run)
|
||||
flags |= TRANSPORT_PUSH_DRY_RUN;
|
||||
if (verbose)
|
||||
flags |= TRANSPORT_PUSH_VERBOSE;
|
||||
if (tags)
|
||||
add_refspec("refs/tags/*");
|
||||
if (all)
|
||||
|
||||
@@ -46,26 +46,14 @@ static inline int is_merge(void)
|
||||
|
||||
static int unmerged_files(void)
|
||||
{
|
||||
char b;
|
||||
ssize_t len;
|
||||
struct child_process cmd;
|
||||
const char *argv_ls_files[] = {"ls-files", "--unmerged", NULL};
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.argv = argv_ls_files;
|
||||
cmd.git_cmd = 1;
|
||||
cmd.out = -1;
|
||||
|
||||
if (start_command(&cmd))
|
||||
die("Could not run sub-command: git ls-files");
|
||||
|
||||
len = xread(cmd.out, &b, 1);
|
||||
if (len < 0)
|
||||
die("Could not read output from git ls-files: %s",
|
||||
strerror(errno));
|
||||
finish_command(&cmd);
|
||||
|
||||
return len;
|
||||
int i;
|
||||
read_cache();
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (ce_stage(ce))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
|
||||
@@ -107,26 +95,34 @@ static void print_new_head_line(struct commit *commit)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int update_index_refresh(void)
|
||||
static int update_index_refresh(int fd, struct lock_file *index_lock)
|
||||
{
|
||||
const char *argv_update_index[] = {"update-index", "--refresh", NULL};
|
||||
return run_command_v_opt(argv_update_index, RUN_GIT_CMD);
|
||||
}
|
||||
int result;
|
||||
|
||||
struct update_cb_data {
|
||||
int index_fd;
|
||||
struct lock_file *lock;
|
||||
int exit_code;
|
||||
};
|
||||
if (!index_lock) {
|
||||
index_lock = xcalloc(1, sizeof(struct lock_file));
|
||||
fd = hold_locked_index(index_lock, 1);
|
||||
}
|
||||
|
||||
if (read_cache() < 0)
|
||||
return error("Could not read index");
|
||||
result = refresh_cache(0) ? 1 : 0;
|
||||
if (write_cache(fd, active_cache, active_nr) ||
|
||||
close(fd) ||
|
||||
commit_locked_index(index_lock))
|
||||
return error ("Could not refresh index");
|
||||
return result;
|
||||
}
|
||||
|
||||
static void update_index_from_diff(struct diff_queue_struct *q,
|
||||
struct diff_options *opt, void *data)
|
||||
{
|
||||
int i;
|
||||
struct update_cb_data *cb = data;
|
||||
int *discard_flag = data;
|
||||
|
||||
/* do_diff_cache() mangled the index */
|
||||
discard_cache();
|
||||
*discard_flag = 1;
|
||||
read_cache();
|
||||
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
@@ -140,34 +136,33 @@ static void update_index_from_diff(struct diff_queue_struct *q,
|
||||
} else
|
||||
remove_file_from_cache(one->path);
|
||||
}
|
||||
|
||||
cb->exit_code = write_cache(cb->index_fd, active_cache, active_nr) ||
|
||||
close(cb->index_fd) ||
|
||||
commit_locked_index(cb->lock);
|
||||
}
|
||||
|
||||
static int read_from_tree(const char *prefix, const char **argv,
|
||||
unsigned char *tree_sha1)
|
||||
{
|
||||
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
|
||||
int index_fd, index_was_discarded = 0;
|
||||
struct diff_options opt;
|
||||
struct update_cb_data cb;
|
||||
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
|
||||
opt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
opt.format_callback = update_index_from_diff;
|
||||
opt.format_callback_data = &cb;
|
||||
opt.format_callback_data = &index_was_discarded;
|
||||
|
||||
cb.lock = xcalloc(1, sizeof(struct lock_file));
|
||||
cb.index_fd = hold_locked_index(cb.lock, 1);
|
||||
cb.exit_code = 0;
|
||||
index_fd = hold_locked_index(lock, 1);
|
||||
index_was_discarded = 0;
|
||||
read_cache();
|
||||
if (do_diff_cache(tree_sha1, &opt))
|
||||
return 1;
|
||||
diffcore_std(&opt);
|
||||
diff_flush(&opt);
|
||||
|
||||
return cb.exit_code;
|
||||
if (!index_was_discarded)
|
||||
/* The index is still clobbered from do_diff_cache() */
|
||||
discard_cache();
|
||||
return update_index_refresh(index_fd, lock);
|
||||
}
|
||||
|
||||
static void prepend_reflog_action(const char *action, char *buf, size_t size)
|
||||
@@ -243,9 +238,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
else if (reset_type != NONE)
|
||||
die("Cannot do %s reset with paths.",
|
||||
reset_type_names[reset_type]);
|
||||
if (read_from_tree(prefix, argv + i, sha1))
|
||||
return 1;
|
||||
return update_index_refresh() ? 1 : 0;
|
||||
return read_from_tree(prefix, argv + i, sha1);
|
||||
}
|
||||
if (reset_type == NONE)
|
||||
reset_type = MIXED; /* by default */
|
||||
@@ -282,7 +275,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
|
||||
case SOFT: /* Nothing else to do. */
|
||||
break;
|
||||
case MIXED: /* Report what has not been updated. */
|
||||
update_index_refresh();
|
||||
update_index_refresh(0, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ static const char rev_list_usage[] =
|
||||
" --remove-empty\n"
|
||||
" --all\n"
|
||||
" --stdin\n"
|
||||
" --quiet\n"
|
||||
" ordering output:\n"
|
||||
" --topo-order\n"
|
||||
" --date-order\n"
|
||||
@@ -50,6 +51,7 @@ static int show_timestamp;
|
||||
static int hdr_termination;
|
||||
static const char *header_prefix;
|
||||
|
||||
static void finish_commit(struct commit *commit);
|
||||
static void show_commit(struct commit *commit)
|
||||
{
|
||||
if (show_timestamp)
|
||||
@@ -93,6 +95,11 @@ static void show_commit(struct commit *commit)
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
maybe_flush_or_die(stdout, "stdout");
|
||||
finish_commit(commit);
|
||||
}
|
||||
|
||||
static void finish_commit(struct commit *commit)
|
||||
{
|
||||
if (commit->parents) {
|
||||
free_commit_list(commit->parents);
|
||||
commit->parents = NULL;
|
||||
@@ -101,6 +108,12 @@ static void show_commit(struct commit *commit)
|
||||
commit->buffer = NULL;
|
||||
}
|
||||
|
||||
static void finish_object(struct object_array_entry *p)
|
||||
{
|
||||
if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
|
||||
die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
|
||||
}
|
||||
|
||||
static void show_object(struct object_array_entry *p)
|
||||
{
|
||||
/* An object with name "foo\n0000000..." can be used to
|
||||
@@ -108,9 +121,7 @@ static void show_object(struct object_array_entry *p)
|
||||
*/
|
||||
const char *ep = strchr(p->name, '\n');
|
||||
|
||||
if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
|
||||
die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
|
||||
|
||||
finish_object(p);
|
||||
if (ep) {
|
||||
printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
|
||||
(int) (ep - p->name),
|
||||
@@ -527,6 +538,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
int read_from_stdin = 0;
|
||||
int bisect_show_vars = 0;
|
||||
int bisect_find_all = 0;
|
||||
int quiet = 0;
|
||||
|
||||
git_config(git_default_config);
|
||||
init_revisions(&revs, prefix);
|
||||
@@ -565,6 +577,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
read_revisions_from_stdin(&revs);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--quiet")) {
|
||||
quiet = 1;
|
||||
continue;
|
||||
}
|
||||
usage(rev_list_usage);
|
||||
|
||||
}
|
||||
@@ -640,7 +656,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
}
|
||||
|
||||
traverse_commit_list(&revs, show_commit, show_object);
|
||||
traverse_commit_list(&revs,
|
||||
quiet ? finish_commit : show_commit,
|
||||
quiet ? finish_object : show_object);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -246,7 +246,9 @@ static int revert_or_cherry_pick(int argc, const char **argv)
|
||||
if (no_commit) {
|
||||
/*
|
||||
* We do not intend to commit immediately. We just want to
|
||||
* merge the differences in.
|
||||
* merge the differences in, so let's compute the tree
|
||||
* that represents the "current" state for merge-recursive
|
||||
* to work on.
|
||||
*/
|
||||
if (write_tree(head, 0, NULL))
|
||||
die ("Your index file is unmerged.");
|
||||
@@ -256,7 +258,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
|
||||
if (get_sha1("HEAD", head))
|
||||
die ("You do not have a valid HEAD");
|
||||
wt_status_prepare(&s);
|
||||
if (s.commitable || s.workdir_dirty)
|
||||
if (s.commitable)
|
||||
die ("Dirty index: cannot %s", me);
|
||||
discard_cache();
|
||||
}
|
||||
|
||||
@@ -246,9 +246,37 @@ static int git_tag_config(const char *var, const char *value)
|
||||
return git_default_config(var, value);
|
||||
}
|
||||
|
||||
static void write_tag_body(int fd, const unsigned char *sha1)
|
||||
{
|
||||
unsigned long size;
|
||||
enum object_type type;
|
||||
char *buf, *sp, *eob;
|
||||
size_t len;
|
||||
|
||||
buf = read_sha1_file(sha1, &type, &size);
|
||||
if (!buf)
|
||||
return;
|
||||
/* skip header */
|
||||
sp = strstr(buf, "\n\n");
|
||||
|
||||
if (!sp || !size || type != OBJ_TAG) {
|
||||
free(buf);
|
||||
return;
|
||||
}
|
||||
sp += 2; /* skip the 2 LFs */
|
||||
eob = strstr(sp, "\n" PGP_SIGNATURE "\n");
|
||||
if (eob)
|
||||
len = eob - sp;
|
||||
else
|
||||
len = buf + size - sp;
|
||||
write_or_die(fd, sp, len);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void create_tag(const unsigned char *object, const char *tag,
|
||||
struct strbuf *buf, int message, int sign,
|
||||
unsigned char *result)
|
||||
unsigned char *prev, unsigned char *result)
|
||||
{
|
||||
enum object_type type;
|
||||
char header_buf[1024];
|
||||
@@ -281,7 +309,11 @@ static void create_tag(const unsigned char *object, const char *tag,
|
||||
if (fd < 0)
|
||||
die("could not create file '%s': %s",
|
||||
path, strerror(errno));
|
||||
write_or_die(fd, tag_template, strlen(tag_template));
|
||||
|
||||
if (!is_null_sha1(prev))
|
||||
write_tag_body(fd, prev);
|
||||
else
|
||||
write_or_die(fd, tag_template, strlen(tag_template));
|
||||
close(fd);
|
||||
|
||||
launch_editor(path, buf);
|
||||
@@ -418,7 +450,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
|
||||
die("tag '%s' already exists", tag);
|
||||
|
||||
if (annotate)
|
||||
create_tag(object, tag, &buf, message, sign, object);
|
||||
create_tag(object, tag, &buf, message, sign, prev, object);
|
||||
|
||||
lock = lock_any_ref_for_update(ref, prev, 0);
|
||||
if (!lock)
|
||||
|
||||
15
cache.h
15
cache.h
@@ -175,8 +175,8 @@ extern struct index_state the_index;
|
||||
#define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
|
||||
#define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))
|
||||
#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
|
||||
#define ce_match_stat(ce, st, really) ie_match_stat(&the_index, (ce), (st), (really))
|
||||
#define ce_modified(ce, st, really) ie_modified(&the_index, (ce), (st), (really))
|
||||
#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
|
||||
#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
|
||||
#endif
|
||||
|
||||
enum object_type {
|
||||
@@ -268,8 +268,14 @@ extern int remove_file_from_index(struct index_state *, const char *path);
|
||||
extern int add_file_to_index(struct index_state *, const char *path, int verbose);
|
||||
extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
|
||||
extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
|
||||
extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, int);
|
||||
extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);
|
||||
|
||||
/* do stat comparison even if CE_VALID is true */
|
||||
#define CE_MATCH_IGNORE_VALID 01
|
||||
/* do not check the contents but report dirty on racily-clean entries */
|
||||
#define CE_MATCH_RACY_IS_DIRTY 02
|
||||
extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, unsigned int);
|
||||
extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, unsigned 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 index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
|
||||
@@ -581,6 +587,7 @@ extern int pager_in_use;
|
||||
extern int pager_use_color;
|
||||
|
||||
extern char *editor_program;
|
||||
extern char *excludes_file;
|
||||
|
||||
/* base85 */
|
||||
int decode_85(char *dst, const char *line, int linelen);
|
||||
|
||||
@@ -18,7 +18,7 @@ int main(int ac, char **av)
|
||||
|
||||
if (ce_match_stat(ce, &st, 0))
|
||||
dirty++;
|
||||
else if (ce_match_stat(ce, &st, 2))
|
||||
else if (ce_match_stat(ce, &st, CE_MATCH_RACY_IS_DIRTY))
|
||||
racy++;
|
||||
else
|
||||
clean++;
|
||||
|
||||
7
config.c
7
config.c
@@ -432,6 +432,13 @@ int git_default_config(const char *var, const char *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.excludesfile")) {
|
||||
if (!value)
|
||||
die("core.excludesfile without value");
|
||||
excludes_file = xstrdup(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add other config variables here and to Documentation/config.txt. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
18
csum-file.c
18
csum-file.c
@@ -18,7 +18,8 @@ static void sha1flush(struct sha1file *f, unsigned int count)
|
||||
for (;;) {
|
||||
int ret = xwrite(f->fd, buf, count);
|
||||
if (ret > 0) {
|
||||
display_throughput(f->tp, ret);
|
||||
f->total += ret;
|
||||
display_throughput(f->tp, f->total);
|
||||
buf = (char *) buf + ret;
|
||||
count -= ret;
|
||||
if (count)
|
||||
@@ -87,21 +88,12 @@ struct sha1file *sha1fd(int fd, const char *name)
|
||||
|
||||
struct sha1file *sha1fd_throughput(int fd, const char *name, struct progress *tp)
|
||||
{
|
||||
struct sha1file *f;
|
||||
unsigned len;
|
||||
|
||||
f = xmalloc(sizeof(*f));
|
||||
|
||||
len = strlen(name);
|
||||
if (len >= PATH_MAX)
|
||||
die("you wascally wabbit, you");
|
||||
f->namelen = len;
|
||||
memcpy(f->name, name, len+1);
|
||||
|
||||
struct sha1file *f = xmalloc(sizeof(*f));
|
||||
f->fd = fd;
|
||||
f->error = 0;
|
||||
f->offset = 0;
|
||||
f->total = 0;
|
||||
f->tp = tp;
|
||||
f->name = name;
|
||||
f->do_crc = 0;
|
||||
SHA1_Init(&f->ctx);
|
||||
return f;
|
||||
|
||||
@@ -5,11 +5,12 @@ struct progress;
|
||||
|
||||
/* A SHA1-protected file */
|
||||
struct sha1file {
|
||||
int fd, error;
|
||||
unsigned int offset, namelen;
|
||||
int fd;
|
||||
unsigned int offset;
|
||||
SHA_CTX ctx;
|
||||
off_t total;
|
||||
struct progress *tp;
|
||||
char name[PATH_MAX];
|
||||
const char *name;
|
||||
int do_crc;
|
||||
uint32_t crc32;
|
||||
unsigned char buffer[8192];
|
||||
|
||||
20
diff-lib.c
20
diff-lib.c
@@ -173,9 +173,10 @@ static int is_in_index(const char *path)
|
||||
}
|
||||
|
||||
static int handle_diff_files_args(struct rev_info *revs,
|
||||
int argc, const char **argv, int *silent)
|
||||
int argc, const char **argv,
|
||||
unsigned int *options)
|
||||
{
|
||||
*silent = 0;
|
||||
*options = 0;
|
||||
|
||||
/* revs->max_count == -2 means --no-index */
|
||||
while (1 < argc && argv[1][0] == '-') {
|
||||
@@ -192,7 +193,7 @@ static int handle_diff_files_args(struct rev_info *revs,
|
||||
revs->diffopt.no_index = 1;
|
||||
}
|
||||
else if (!strcmp(argv[1], "-q"))
|
||||
*silent = 1;
|
||||
*options |= DIFF_SILENT_ON_REMOVED;
|
||||
else
|
||||
return error("invalid option: %s", argv[1]);
|
||||
argv++; argc--;
|
||||
@@ -305,9 +306,9 @@ int setup_diff_no_index(struct rev_info *revs,
|
||||
|
||||
int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
|
||||
{
|
||||
int silent_on_removed;
|
||||
unsigned int options;
|
||||
|
||||
if (handle_diff_files_args(revs, argc, argv, &silent_on_removed))
|
||||
if (handle_diff_files_args(revs, argc, argv, &options))
|
||||
return -1;
|
||||
|
||||
if (revs->diffopt.no_index) {
|
||||
@@ -329,13 +330,16 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
|
||||
perror("read_cache");
|
||||
return -1;
|
||||
}
|
||||
return run_diff_files(revs, silent_on_removed);
|
||||
return run_diff_files(revs, options);
|
||||
}
|
||||
|
||||
int run_diff_files(struct rev_info *revs, int silent_on_removed)
|
||||
int run_diff_files(struct rev_info *revs, unsigned int option)
|
||||
{
|
||||
int entries, i;
|
||||
int diff_unmerged_stage = revs->max_count;
|
||||
int silent_on_removed = option & DIFF_SILENT_ON_REMOVED;
|
||||
unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)
|
||||
? CE_MATCH_RACY_IS_DIRTY : 0);
|
||||
|
||||
if (diff_unmerged_stage < 0)
|
||||
diff_unmerged_stage = 2;
|
||||
@@ -441,7 +445,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
|
||||
ce->sha1, ce->name, NULL);
|
||||
continue;
|
||||
}
|
||||
changed = ce_match_stat(ce, &st, 0);
|
||||
changed = ce_match_stat(ce, &st, ce_option);
|
||||
if (!changed && !revs->diffopt.find_copies_harder)
|
||||
continue;
|
||||
oldmode = ntohl(ce->ce_mode);
|
||||
|
||||
6
diff.h
6
diff.h
@@ -224,7 +224,11 @@ extern void diff_flush(struct diff_options*);
|
||||
|
||||
extern const char *diff_unique_abbrev(const unsigned char *, int);
|
||||
|
||||
extern int run_diff_files(struct rev_info *revs, int silent_on_removed);
|
||||
/* do not report anything on removed paths */
|
||||
#define DIFF_SILENT_ON_REMOVED 01
|
||||
/* report racily-clean paths as modified */
|
||||
#define DIFF_RACY_IS_MODIFIED 02
|
||||
extern int run_diff_files(struct rev_info *revs, unsigned int option);
|
||||
extern int setup_diff_no_index(struct rev_info *revs,
|
||||
int argc, const char ** argv, int nongit, const char *prefix);
|
||||
extern int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv);
|
||||
|
||||
12
dir.c
12
dir.c
@@ -778,3 +778,15 @@ int remove_dir_recursively(struct strbuf *path, int only_empty)
|
||||
ret = rmdir(path->buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void setup_standard_excludes(struct dir_struct *dir)
|
||||
{
|
||||
const char *path;
|
||||
|
||||
dir->exclude_per_dir = ".gitignore";
|
||||
path = git_path("info/exclude");
|
||||
if (!access(path, R_OK))
|
||||
add_excludes_from_file(dir, path);
|
||||
if (excludes_file && !access(excludes_file, R_OK))
|
||||
add_excludes_from_file(dir, excludes_file);
|
||||
}
|
||||
|
||||
1
dir.h
1
dir.h
@@ -71,6 +71,7 @@ extern struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathna
|
||||
extern char *get_relative_cwd(char *buffer, int size, const char *dir);
|
||||
extern int is_inside_dir(const char *dir);
|
||||
|
||||
extern void setup_standard_excludes(struct dir_struct *dir);
|
||||
extern int remove_dir_recursively(struct strbuf *path, int only_empty);
|
||||
|
||||
#endif
|
||||
|
||||
2
entry.c
2
entry.c
@@ -203,7 +203,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
|
||||
strcpy(path + len, ce->name);
|
||||
|
||||
if (!lstat(path, &st)) {
|
||||
unsigned changed = ce_match_stat(ce, &st, 1);
|
||||
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
|
||||
if (!changed)
|
||||
return 0;
|
||||
if (!state->force) {
|
||||
|
||||
@@ -34,6 +34,7 @@ char *pager_program;
|
||||
int pager_in_use;
|
||||
int pager_use_color = 1;
|
||||
char *editor_program;
|
||||
char *excludes_file;
|
||||
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
|
||||
|
||||
/* This is set by setup_git_dir_gently() and/or git_default_config() */
|
||||
|
||||
@@ -153,13 +153,16 @@ Format of STDIN stream:
|
||||
|
||||
#define PACK_ID_BITS 16
|
||||
#define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
|
||||
#define DEPTH_BITS 13
|
||||
#define MAX_DEPTH ((1<<DEPTH_BITS)-1)
|
||||
|
||||
struct object_entry
|
||||
{
|
||||
struct object_entry *next;
|
||||
uint32_t offset;
|
||||
unsigned type : TYPE_BITS;
|
||||
unsigned pack_id : PACK_ID_BITS;
|
||||
uint32_t type : TYPE_BITS,
|
||||
pack_id : PACK_ID_BITS,
|
||||
depth : DEPTH_BITS;
|
||||
unsigned char sha1[20];
|
||||
};
|
||||
|
||||
@@ -1083,7 +1086,7 @@ static int store_object(
|
||||
unsigned pos = sizeof(hdr) - 1;
|
||||
|
||||
delta_count_by_type[type]++;
|
||||
last->depth++;
|
||||
e->depth = last->depth + 1;
|
||||
|
||||
hdrlen = encode_header(OBJ_OFS_DELTA, deltalen, hdr);
|
||||
write_or_die(pack_data->pack_fd, hdr, hdrlen);
|
||||
@@ -1095,8 +1098,7 @@ static int store_object(
|
||||
write_or_die(pack_data->pack_fd, hdr + pos, sizeof(hdr) - pos);
|
||||
pack_size += sizeof(hdr) - pos;
|
||||
} else {
|
||||
if (last)
|
||||
last->depth = 0;
|
||||
e->depth = 0;
|
||||
hdrlen = encode_header(type, dat->len, hdr);
|
||||
write_or_die(pack_data->pack_fd, hdr, hdrlen);
|
||||
pack_size += hdrlen;
|
||||
@@ -1114,6 +1116,7 @@ static int store_object(
|
||||
strbuf_swap(&last->data, dat);
|
||||
}
|
||||
last->offset = e->offset;
|
||||
last->depth = e->depth;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1160,7 +1163,7 @@ static void load_tree(struct tree_entry *root)
|
||||
if (myoe && myoe->pack_id != MAX_PACK_ID) {
|
||||
if (myoe->type != OBJ_TREE)
|
||||
die("Not a tree: %s", sha1_to_hex(sha1));
|
||||
t->delta_depth = 0;
|
||||
t->delta_depth = myoe->depth;
|
||||
buf = gfi_unpack_entry(myoe, &size);
|
||||
} else {
|
||||
enum object_type type;
|
||||
@@ -2289,8 +2292,11 @@ int main(int argc, const char **argv)
|
||||
}
|
||||
else if (!prefixcmp(a, "--max-pack-size="))
|
||||
max_packsize = strtoumax(a + 16, NULL, 0) * 1024 * 1024;
|
||||
else if (!prefixcmp(a, "--depth="))
|
||||
else if (!prefixcmp(a, "--depth=")) {
|
||||
max_depth = strtoul(a + 8, NULL, 0);
|
||||
if (max_depth > MAX_DEPTH)
|
||||
die("--depth cannot exceed %u", MAX_DEPTH);
|
||||
}
|
||||
else if (!prefixcmp(a, "--active-branches="))
|
||||
max_active_branches = strtoul(a + 18, NULL, 0);
|
||||
else if (!prefixcmp(a, "--import-marks="))
|
||||
|
||||
33
git-clean.sh
33
git-clean.sh
@@ -25,10 +25,7 @@ rmrf="rm -rf --"
|
||||
rm_refuse="echo Not removing"
|
||||
echo1="echo"
|
||||
|
||||
# requireForce used to default to false but now it defaults to true.
|
||||
# IOW, lack of explicit "clean.requireForce = false" is taken as
|
||||
# "clean.requireForce = true".
|
||||
disabled=$(git config --bool clean.requireForce || echo true)
|
||||
disabled=$(git config --bool clean.requireForce)
|
||||
|
||||
while test $# != 0
|
||||
do
|
||||
@@ -37,10 +34,10 @@ do
|
||||
cleandir=1
|
||||
;;
|
||||
-f)
|
||||
disabled=
|
||||
disabled=false
|
||||
;;
|
||||
-n)
|
||||
disabled=
|
||||
disabled=false
|
||||
rmf="echo Would remove"
|
||||
rmrf="echo Would remove"
|
||||
rm_refuse="echo Would not remove"
|
||||
@@ -68,10 +65,17 @@ do
|
||||
shift
|
||||
done
|
||||
|
||||
if [ "$disabled" = true ]; then
|
||||
echo "clean.requireForce set and -n or -f not given; refusing to clean"
|
||||
exit 1
|
||||
fi
|
||||
# requireForce used to default to false but now it defaults to true.
|
||||
# IOW, lack of explicit "clean.requireForce = false" is taken as
|
||||
# "clean.requireForce = true".
|
||||
case "$disabled" in
|
||||
"")
|
||||
die "clean.requireForce not set and -n or -f not given; refusing to clean"
|
||||
;;
|
||||
"true")
|
||||
die "clean.requireForce set and -n or -f not given; refusing to clean"
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$ignored,$ignoredonly" in
|
||||
1,1) usage;;
|
||||
@@ -79,15 +83,22 @@ esac
|
||||
|
||||
if [ -z "$ignored" ]; then
|
||||
excl="--exclude-per-directory=.gitignore"
|
||||
excl_info= excludes_file=
|
||||
if [ -f "$GIT_DIR/info/exclude" ]; then
|
||||
excl_info="--exclude-from=$GIT_DIR/info/exclude"
|
||||
fi
|
||||
if cfg_excl=$(git config core.excludesfile) && test -f "$cfg_excl"
|
||||
then
|
||||
excludes_file="--exclude-from=$cfg_excl"
|
||||
fi
|
||||
if [ "$ignoredonly" ]; then
|
||||
excl="$excl --ignored"
|
||||
fi
|
||||
fi
|
||||
|
||||
git ls-files --others --directory $excl ${excl_info:+"$excl_info"} -- "$@" |
|
||||
git ls-files --others --directory \
|
||||
$excl ${excl_info:+"$excl_info"} ${excludes_file:+"$excludes_file"} \
|
||||
-- "$@" |
|
||||
while read -r file; do
|
||||
if [ -d "$file" -a ! -L "$file" ]; then
|
||||
if [ -z "$cleandir" ]; then
|
||||
|
||||
@@ -26,7 +26,7 @@ refuse_partial () {
|
||||
}
|
||||
|
||||
TMP_INDEX=
|
||||
THIS_INDEX="$GIT_DIR/index"
|
||||
THIS_INDEX="${GIT_INDEX_FILE:-$GIT_DIR/index}"
|
||||
NEXT_INDEX="$GIT_DIR/next-index$$"
|
||||
rm -f "$NEXT_INDEX"
|
||||
save_index () {
|
||||
@@ -282,9 +282,9 @@ unset only
|
||||
case "$all,$interactive,$also,$#" in
|
||||
*t,*t,*)
|
||||
die "Cannot use -a, --interactive or -i at the same time." ;;
|
||||
t,,[1-9]*)
|
||||
t,,,[1-9]*)
|
||||
die "Paths with -a does not make sense." ;;
|
||||
,t,[1-9]*)
|
||||
,t,,[1-9]*)
|
||||
die "Paths with --interactive does not make sense." ;;
|
||||
,,t,0)
|
||||
die "No paths with -i does not make sense." ;;
|
||||
|
||||
@@ -194,6 +194,22 @@ void *gitmemmem(const void *haystack, size_t haystacklen,
|
||||
const void *needle, size_t needlelen);
|
||||
#endif
|
||||
|
||||
#ifdef __GLIBC_PREREQ
|
||||
#if __GLIBC_PREREQ(2, 1)
|
||||
#define HAVE_STRCHRNUL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRCHRNUL
|
||||
#define strchrnul gitstrchrnul
|
||||
static inline char *gitstrchrnul(const char *s, int c)
|
||||
{
|
||||
while (*s && *s != c)
|
||||
s++;
|
||||
return (char *)s;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern void release_pack_memory(size_t, int);
|
||||
|
||||
static inline char* xstrdup(const char *str)
|
||||
|
||||
@@ -1,28 +1,42 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
# Known limitations:
|
||||
# - does not propagate permissions
|
||||
# - error handling has not been extensively tested
|
||||
#
|
||||
|
||||
use strict;
|
||||
use Getopt::Std;
|
||||
use File::Temp qw(tempdir);
|
||||
use Data::Dumper;
|
||||
use File::Basename qw(basename dirname);
|
||||
|
||||
unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
|
||||
die "GIT_DIR is not defined or is unreadable";
|
||||
}
|
||||
our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w);
|
||||
|
||||
our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u);
|
||||
|
||||
getopts('uhPpvcfam:d:');
|
||||
getopts('uhPpvcfam:d:w:');
|
||||
|
||||
$opt_h && usage();
|
||||
|
||||
die "Need at least one commit identifier!" unless @ARGV;
|
||||
|
||||
if ($opt_w) {
|
||||
unless ($ENV{GIT_DIR}) {
|
||||
# Remember where our GIT_DIR is before changing to CVS checkout
|
||||
my $gd =`git-rev-parse --git-dir`;
|
||||
chomp($gd);
|
||||
if ($gd eq '.git') {
|
||||
my $wd = `pwd`;
|
||||
chomp($wd);
|
||||
$gd = $wd."/.git" ;
|
||||
}
|
||||
$ENV{GIT_DIR} = $gd;
|
||||
}
|
||||
|
||||
if (! -d $opt_w."/CVS" ) {
|
||||
die "$opt_w is not a CVS checkout";
|
||||
}
|
||||
chdir $opt_w or die "Cannot change to CVS checkout at $opt_w";
|
||||
}
|
||||
unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
|
||||
die "GIT_DIR is not defined or is unreadable";
|
||||
}
|
||||
|
||||
|
||||
my @cvs;
|
||||
if ($opt_d) {
|
||||
@cvs = ('cvs', '-d', $opt_d);
|
||||
@@ -274,6 +288,7 @@ if ($dirtypatch) {
|
||||
print "You'll need to apply the patch in .cvsexportcommit.diff manually\n";
|
||||
print "using a patch program. After applying the patch and resolving the\n";
|
||||
print "problems you may commit using:";
|
||||
print "\n cd \"$opt_w\"" if $opt_w;
|
||||
print "\n $cmd\n\n";
|
||||
exit(1);
|
||||
}
|
||||
@@ -301,7 +316,7 @@ sleep(1);
|
||||
|
||||
sub usage {
|
||||
print STDERR <<END;
|
||||
Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-m msgprefix] [ parent ] commit
|
||||
Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-u] [-w cvsworkdir] [-m msgprefix] [ parent ] commit
|
||||
END
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -223,7 +223,8 @@ sub conn {
|
||||
}
|
||||
}
|
||||
|
||||
$user="anonymous" unless defined $user;
|
||||
# if username is not explicit in CVSROOT, then use current user, as cvs would
|
||||
$user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user;
|
||||
my $rr2 = "-";
|
||||
unless ($port) {
|
||||
$rr2 = ":pserver:$user\@$serv:$repo";
|
||||
|
||||
11
git-merge.sh
11
git-merge.sh
@@ -46,20 +46,19 @@ esac
|
||||
|
||||
dropsave() {
|
||||
rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
|
||||
"$GIT_DIR/MERGE_SAVE" || exit 1
|
||||
"$GIT_DIR/MERGE_STASH" || exit 1
|
||||
}
|
||||
|
||||
savestate() {
|
||||
# Stash away any local modifications.
|
||||
git diff-index -z --name-only $head |
|
||||
cpio -0 -o >"$GIT_DIR/MERGE_SAVE"
|
||||
git stash create >"$GIT_DIR/MERGE_STASH"
|
||||
}
|
||||
|
||||
restorestate() {
|
||||
if test -f "$GIT_DIR/MERGE_SAVE"
|
||||
if test -f "$GIT_DIR/MERGE_STASH"
|
||||
then
|
||||
git reset --hard $head >/dev/null
|
||||
cpio -iuv <"$GIT_DIR/MERGE_SAVE"
|
||||
git stash apply $(cat "$GIT_DIR/MERGE_STASH")
|
||||
git update-index --refresh >/dev/null
|
||||
fi
|
||||
}
|
||||
@@ -455,7 +454,7 @@ case "$use_strategies" in
|
||||
single_strategy=no
|
||||
;;
|
||||
*)
|
||||
rm -f "$GIT_DIR/MERGE_SAVE"
|
||||
rm -f "$GIT_DIR/MERGE_STASH"
|
||||
single_strategy=yes
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -87,7 +87,7 @@ call_merge () {
|
||||
cmt="$(cat "$dotest/cmt.$1")"
|
||||
echo "$cmt" > "$dotest/current"
|
||||
hd=$(git rev-parse --verify HEAD)
|
||||
cmt_name=$(git symbolic-ref HEAD)
|
||||
cmt_name=$(git symbolic-ref HEAD 2> /dev/null || echo HEAD)
|
||||
msgnum=$(cat "$dotest/msgnum")
|
||||
end=$(cat "$dotest/end")
|
||||
eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"'
|
||||
@@ -115,7 +115,24 @@ call_merge () {
|
||||
esac
|
||||
}
|
||||
|
||||
move_to_original_branch () {
|
||||
test -z "$head_name" &&
|
||||
head_name="$(cat "$dotest"/head-name)" &&
|
||||
onto="$(cat "$dotest"/onto)" &&
|
||||
orig_head="$(cat "$dotest"/orig-head)"
|
||||
case "$head_name" in
|
||||
refs/*)
|
||||
message="rebase finished: $head_name onto $onto"
|
||||
git update-ref -m "$message" \
|
||||
$head_name $(git rev-parse HEAD) $orig_head &&
|
||||
git symbolic-ref HEAD $head_name ||
|
||||
die "Could not move back to $head_name"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
finish_rb_merge () {
|
||||
move_to_original_branch
|
||||
rm -r "$dotest"
|
||||
echo "All done."
|
||||
}
|
||||
@@ -153,7 +170,11 @@ do
|
||||
finish_rb_merge
|
||||
exit
|
||||
fi
|
||||
git am --resolved --3way --resolvemsg="$RESOLVEMSG"
|
||||
head_name=$(cat .dotest/head-name) &&
|
||||
onto=$(cat .dotest/onto) &&
|
||||
orig_head=$(cat .dotest/orig-head) &&
|
||||
git am --resolved --3way --resolvemsg="$RESOLVEMSG" &&
|
||||
move_to_original_branch
|
||||
exit
|
||||
;;
|
||||
--skip)
|
||||
@@ -173,16 +194,23 @@ do
|
||||
finish_rb_merge
|
||||
exit
|
||||
fi
|
||||
git am -3 --skip --resolvemsg="$RESOLVEMSG"
|
||||
head_name=$(cat .dotest/head-name) &&
|
||||
onto=$(cat .dotest/onto) &&
|
||||
orig_head=$(cat .dotest/orig-head) &&
|
||||
git am -3 --skip --resolvemsg="$RESOLVEMSG" &&
|
||||
move_to_original_branch
|
||||
exit
|
||||
;;
|
||||
--abort)
|
||||
git rerere clear
|
||||
if test -d "$dotest"
|
||||
then
|
||||
move_to_original_branch
|
||||
rm -r "$dotest"
|
||||
elif test -d .dotest
|
||||
then
|
||||
dotest=.dotest
|
||||
move_to_original_branch
|
||||
rm -r .dotest
|
||||
else
|
||||
die "No rebase in progress?"
|
||||
@@ -318,6 +346,19 @@ then
|
||||
GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
|
||||
fi
|
||||
|
||||
# move to a detached HEAD
|
||||
orig_head=$(git rev-parse HEAD^0)
|
||||
head_name=$(git symbolic-ref HEAD 2> /dev/null)
|
||||
case "$head_name" in
|
||||
'')
|
||||
head_name="detached HEAD"
|
||||
;;
|
||||
*)
|
||||
git checkout "$orig_head" > /dev/null 2>&1 ||
|
||||
die "could not detach HEAD"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
|
||||
echo "First, rewinding head to replay your work on top of it..."
|
||||
git-reset --hard "$onto"
|
||||
@@ -327,14 +368,21 @@ git-reset --hard "$onto"
|
||||
if test "$mb" = "$branch"
|
||||
then
|
||||
echo >&2 "Fast-forwarded $branch_name to $onto_name."
|
||||
move_to_original_branch
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$do_merge"
|
||||
then
|
||||
git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
|
||||
git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG"
|
||||
exit $?
|
||||
git am $git_am_opt --binary -3 -k --resolvemsg="$RESOLVEMSG" &&
|
||||
move_to_original_branch
|
||||
ret=$?
|
||||
test 0 != $ret -a -d .dotest &&
|
||||
echo $head_name > .dotest/head-name &&
|
||||
echo $onto > .dotest/onto &&
|
||||
echo $orig_head > .dotest/orig-head
|
||||
exit $ret
|
||||
fi
|
||||
|
||||
# start doing a rebase with git-merge
|
||||
@@ -343,8 +391,10 @@ fi
|
||||
mkdir -p "$dotest"
|
||||
echo "$onto" > "$dotest/onto"
|
||||
echo "$onto_name" > "$dotest/onto_name"
|
||||
prev_head=`git rev-parse HEAD^0`
|
||||
prev_head=$orig_head
|
||||
echo "$prev_head" > "$dotest/prev_head"
|
||||
echo "$orig_head" > "$dotest/orig-head"
|
||||
echo "$head_name" > "$dotest/head-name"
|
||||
|
||||
msgnum=0
|
||||
for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD`
|
||||
|
||||
34
git-stash.sh
34
git-stash.sh
@@ -21,23 +21,17 @@ no_changes () {
|
||||
clear_stash () {
|
||||
if current=$(git rev-parse --verify $ref_stash 2>/dev/null)
|
||||
then
|
||||
git update-ref -d refs/stash $current
|
||||
git update-ref -d $ref_stash $current
|
||||
fi
|
||||
}
|
||||
|
||||
save_stash () {
|
||||
create_stash () {
|
||||
stash_msg="$1"
|
||||
|
||||
if no_changes
|
||||
then
|
||||
echo >&2 'No local changes to save'
|
||||
exit 0
|
||||
fi
|
||||
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)
|
||||
@@ -84,6 +78,23 @@ save_stash () {
|
||||
w_commit=$(printf '%s\n' "$stash_msg" |
|
||||
git commit-tree $w_tree -p $b_commit -p $i_commit) ||
|
||||
die "Cannot record working tree state"
|
||||
}
|
||||
|
||||
save_stash () {
|
||||
stash_msg="$1"
|
||||
|
||||
if no_changes
|
||||
then
|
||||
echo >&2 'No local changes to save'
|
||||
exit 0
|
||||
fi
|
||||
test -f "$GIT_DIR/logs/$ref_stash" ||
|
||||
clear_stash || die "Cannot initialize stash"
|
||||
|
||||
create_stash "$stash_msg"
|
||||
|
||||
# Make sure the reflog for stash is kept.
|
||||
: >>"$GIT_DIR/logs/$ref_stash"
|
||||
|
||||
git update-ref -m "$stash_msg" $ref_stash $w_commit ||
|
||||
die "Cannot save the current status"
|
||||
@@ -202,6 +213,13 @@ apply)
|
||||
clear)
|
||||
clear_stash
|
||||
;;
|
||||
create)
|
||||
if test $# -gt 0 && test "$1" = create
|
||||
then
|
||||
shift
|
||||
fi
|
||||
create_stash "$*" && echo "$w_commit"
|
||||
;;
|
||||
help | usage)
|
||||
usage
|
||||
;;
|
||||
|
||||
27
git-svn.perl
27
git-svn.perl
@@ -390,6 +390,9 @@ sub cmd_set_tree {
|
||||
|
||||
sub cmd_dcommit {
|
||||
my $head = shift;
|
||||
git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) }
|
||||
'Cannot dcommit with a dirty index. Commit your changes first'
|
||||
. "or stash them with `git stash'.\n";
|
||||
$head ||= 'HEAD';
|
||||
my @refs;
|
||||
my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs);
|
||||
@@ -3220,6 +3223,25 @@ sub _auth_providers () {
|
||||
]
|
||||
}
|
||||
|
||||
sub escape_uri_only {
|
||||
my ($uri) = @_;
|
||||
my @tmp;
|
||||
foreach (split m{/}, $uri) {
|
||||
s/([^\w.-])/sprintf("%%%02X",ord($1))/eg;
|
||||
push @tmp, $_;
|
||||
}
|
||||
join('/', @tmp);
|
||||
}
|
||||
|
||||
sub escape_url {
|
||||
my ($url) = @_;
|
||||
if ($url =~ m#^(https?)://([^/]+)(.*)$#) {
|
||||
my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3));
|
||||
$url = "$scheme://$domain$uri";
|
||||
}
|
||||
$url;
|
||||
}
|
||||
|
||||
sub new {
|
||||
my ($class, $url) = @_;
|
||||
$url =~ s!/+$!!;
|
||||
@@ -3252,10 +3274,11 @@ sub new {
|
||||
$Git::SVN::Prompt::_no_auth_cache = 1;
|
||||
}
|
||||
} # no warnings 'once'
|
||||
my $self = SVN::Ra->new(url => $url, auth => $baton,
|
||||
my $self = SVN::Ra->new(url => escape_url($url), auth => $baton,
|
||||
config => $config,
|
||||
pool => SVN::Pool->new,
|
||||
auth_provider_callbacks => $callbacks);
|
||||
$self->{url} = $url;
|
||||
$self->{svn_path} = $url;
|
||||
$self->{repos_root} = $self->get_repos_root;
|
||||
$self->{svn_path} =~ s#^\Q$self->{repos_root}\E(/|$)##;
|
||||
@@ -3381,7 +3404,7 @@ sub gs_do_switch {
|
||||
|
||||
my $full_url = $self->{url};
|
||||
my $old_url = $full_url;
|
||||
$full_url .= "/$path" if length $path;
|
||||
$full_url .= '/' . escape_uri_only($path) if length $path;
|
||||
my ($ra, $reparented);
|
||||
if ($old_url ne $full_url) {
|
||||
if ($old_url !~ m#^svn(\+ssh)?://#) {
|
||||
|
||||
@@ -1856,7 +1856,7 @@ sub parse_date {
|
||||
$date{'mday-time'} = sprintf "%d %s %02d:%02d",
|
||||
$mday, $months[$mon], $hour ,$min;
|
||||
$date{'iso-8601'} = sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ",
|
||||
1900+$year, $mon, $mday, $hour ,$min, $sec;
|
||||
1900+$year, 1+$mon, $mday, $hour ,$min, $sec;
|
||||
|
||||
$tz =~ m/^([+\-][0-9][0-9])([0-9][0-9])$/;
|
||||
my $local = $epoch + ((int $1 + ($2/60)) * 3600);
|
||||
|
||||
@@ -42,6 +42,8 @@ int main(int argc, char **argv)
|
||||
int prefix_length = -1;
|
||||
int no_more_flags = 0;
|
||||
|
||||
git_config(git_default_config);
|
||||
|
||||
for (i = 1 ; i < argc; i++) {
|
||||
if (!no_more_flags && argv[i][0] == '-') {
|
||||
if (!strcmp(argv[i], "-t")) {
|
||||
|
||||
16
index-pack.c
16
index-pack.c
@@ -87,9 +87,9 @@ static void *fill(int min)
|
||||
die("early EOF");
|
||||
die("read error on input: %s", strerror(errno));
|
||||
}
|
||||
if (from_stdin)
|
||||
display_throughput(progress, ret);
|
||||
input_len += ret;
|
||||
if (from_stdin)
|
||||
display_throughput(progress, consumed_bytes + input_len);
|
||||
} while (input_len < min);
|
||||
return input_buffer;
|
||||
}
|
||||
@@ -256,7 +256,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
|
||||
|
||||
static void *get_data_from_pack(struct object_entry *obj)
|
||||
{
|
||||
unsigned long from = obj[0].idx.offset + obj[0].hdr_size;
|
||||
off_t from = obj[0].idx.offset + obj[0].hdr_size;
|
||||
unsigned long len = obj[1].idx.offset - from;
|
||||
unsigned long rdy = 0;
|
||||
unsigned char *src, *data;
|
||||
@@ -792,6 +792,7 @@ int main(int argc, char **argv)
|
||||
flush();
|
||||
} else {
|
||||
if (fix_thin_pack) {
|
||||
char msg[48];
|
||||
int nr_unresolved = nr_deltas - nr_resolved_deltas;
|
||||
int nr_objects_initial = nr_objects;
|
||||
if (nr_unresolved <= 0)
|
||||
@@ -800,12 +801,11 @@ int main(int argc, char **argv)
|
||||
(nr_objects + nr_unresolved + 1)
|
||||
* sizeof(*objects));
|
||||
fix_unresolved_deltas(nr_unresolved);
|
||||
stop_progress(&progress);
|
||||
if (verbose)
|
||||
fprintf(stderr, "%d objects were added to complete this thin pack.\n",
|
||||
nr_objects - nr_objects_initial);
|
||||
sprintf(msg, "completed with %d local objects",
|
||||
nr_objects - nr_objects_initial);
|
||||
stop_progress_msg(&progress, msg);
|
||||
fixup_pack_header_footer(output_fd, sha1,
|
||||
curr_pack, nr_objects);
|
||||
curr_pack, nr_objects);
|
||||
}
|
||||
if (nr_deltas != nr_resolved_deltas)
|
||||
die("pack has %d unresolved deltas",
|
||||
|
||||
@@ -170,4 +170,11 @@ void traverse_commit_list(struct rev_info *revs,
|
||||
}
|
||||
for (i = 0; i < objects.nr; i++)
|
||||
show_object(&objects.objects[i]);
|
||||
free(objects.objects);
|
||||
if (revs->pending.nr) {
|
||||
free(revs->pending.objects);
|
||||
revs->pending.nr = 0;
|
||||
revs->pending.alloc = 0;
|
||||
revs->pending.objects = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,8 +119,8 @@ static int parse_long_opt(struct optparse_t *p, const char *arg,
|
||||
const struct option *options)
|
||||
{
|
||||
const char *arg_end = strchr(arg, '=');
|
||||
const struct option *abbrev_option = NULL;
|
||||
int abbrev_flags = 0;
|
||||
const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
|
||||
int abbrev_flags = 0, ambiguous_flags = 0;
|
||||
|
||||
if (!arg_end)
|
||||
arg_end = arg + strlen(arg);
|
||||
@@ -137,16 +137,16 @@ static int parse_long_opt(struct optparse_t *p, const char *arg,
|
||||
/* abbreviated? */
|
||||
if (!strncmp(options->long_name, arg, arg_end - arg)) {
|
||||
is_abbreviated:
|
||||
if (abbrev_option)
|
||||
return error("Ambiguous option: %s "
|
||||
"(could be --%s%s or --%s%s)",
|
||||
arg,
|
||||
(flags & OPT_UNSET) ?
|
||||
"no-" : "",
|
||||
options->long_name,
|
||||
(abbrev_flags & OPT_UNSET) ?
|
||||
"no-" : "",
|
||||
abbrev_option->long_name);
|
||||
if (abbrev_option) {
|
||||
/*
|
||||
* If this is abbreviated, it is
|
||||
* ambiguous. So when there is no
|
||||
* exact match later, we need to
|
||||
* error out.
|
||||
*/
|
||||
ambiguous_option = abbrev_option;
|
||||
ambiguous_flags = abbrev_flags;
|
||||
}
|
||||
if (!(flags & OPT_UNSET) && *arg_end)
|
||||
p->opt = arg_end + 1;
|
||||
abbrev_option = options;
|
||||
@@ -176,6 +176,15 @@ is_abbreviated:
|
||||
}
|
||||
return get_value(p, options, flags);
|
||||
}
|
||||
|
||||
if (ambiguous_option)
|
||||
return error("Ambiguous option: %s "
|
||||
"(could be --%s%s or --%s%s)",
|
||||
arg,
|
||||
(ambiguous_flags & OPT_UNSET) ? "no-" : "",
|
||||
ambiguous_option->long_name,
|
||||
(abbrev_flags & OPT_UNSET) ? "no-" : "",
|
||||
abbrev_option->long_name);
|
||||
if (abbrev_option)
|
||||
return get_value(p, abbrev_option, abbrev_flags);
|
||||
return error("unknown option `%s'", arg);
|
||||
|
||||
360
pretty.c
360
pretty.c
@@ -1,6 +1,5 @@
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "interpolate.h"
|
||||
#include "utf8.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
@@ -283,7 +282,8 @@ static char *logmsg_reencode(const struct commit *commit,
|
||||
return out;
|
||||
}
|
||||
|
||||
static void fill_person(struct interp *table, const char *msg, int len)
|
||||
static void format_person_part(struct strbuf *sb, char part,
|
||||
const char *msg, int len)
|
||||
{
|
||||
int start, end, tz = 0;
|
||||
unsigned long date;
|
||||
@@ -295,7 +295,10 @@ static void fill_person(struct interp *table, const char *msg, int len)
|
||||
start = end + 1;
|
||||
while (end > 0 && isspace(msg[end - 1]))
|
||||
end--;
|
||||
table[0].value = xmemdupz(msg, end);
|
||||
if (part == 'n') { /* name */
|
||||
strbuf_add(sb, msg, end);
|
||||
return;
|
||||
}
|
||||
|
||||
if (start >= len)
|
||||
return;
|
||||
@@ -307,7 +310,10 @@ static void fill_person(struct interp *table, const char *msg, int len)
|
||||
if (end >= len)
|
||||
return;
|
||||
|
||||
table[1].value = xmemdupz(msg + start, end - start);
|
||||
if (part == 'e') { /* email */
|
||||
strbuf_add(sb, msg + start, end - start);
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse date */
|
||||
for (start = end + 1; start < len && isspace(msg[start]); start++)
|
||||
@@ -318,7 +324,10 @@ static void fill_person(struct interp *table, const char *msg, int len)
|
||||
if (msg + start == ep)
|
||||
return;
|
||||
|
||||
table[5].value = xmemdupz(msg + start, ep - (msg + start));
|
||||
if (part == 't') { /* date, UNIX timestamp */
|
||||
strbuf_add(sb, msg + start, ep - (msg + start));
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse tz */
|
||||
for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
|
||||
@@ -329,115 +338,66 @@ static void fill_person(struct interp *table, const char *msg, int len)
|
||||
tz = -tz;
|
||||
}
|
||||
|
||||
interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL));
|
||||
interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822));
|
||||
interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE));
|
||||
interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
|
||||
switch (part) {
|
||||
case 'd': /* date */
|
||||
strbuf_addstr(sb, show_date(date, tz, DATE_NORMAL));
|
||||
return;
|
||||
case 'D': /* date, RFC2822 style */
|
||||
strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822));
|
||||
return;
|
||||
case 'r': /* date, relative */
|
||||
strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE));
|
||||
return;
|
||||
case 'i': /* date, ISO 8601 */
|
||||
strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void format_commit_message(const struct commit *commit,
|
||||
const void *format, struct strbuf *sb)
|
||||
struct chunk {
|
||||
size_t off;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct format_commit_context {
|
||||
const struct commit *commit;
|
||||
|
||||
/* These offsets are relative to the start of the commit message. */
|
||||
int commit_header_parsed;
|
||||
struct chunk subject;
|
||||
struct chunk author;
|
||||
struct chunk committer;
|
||||
struct chunk encoding;
|
||||
size_t body_off;
|
||||
|
||||
/* The following ones are relative to the result struct strbuf. */
|
||||
struct chunk abbrev_commit_hash;
|
||||
struct chunk abbrev_tree_hash;
|
||||
struct chunk abbrev_parent_hashes;
|
||||
};
|
||||
|
||||
static int add_again(struct strbuf *sb, struct chunk *chunk)
|
||||
{
|
||||
struct interp table[] = {
|
||||
{ "%H" }, /* commit hash */
|
||||
{ "%h" }, /* abbreviated commit hash */
|
||||
{ "%T" }, /* tree hash */
|
||||
{ "%t" }, /* abbreviated tree hash */
|
||||
{ "%P" }, /* parent hashes */
|
||||
{ "%p" }, /* abbreviated parent hashes */
|
||||
{ "%an" }, /* author name */
|
||||
{ "%ae" }, /* author email */
|
||||
{ "%ad" }, /* author date */
|
||||
{ "%aD" }, /* author date, RFC2822 style */
|
||||
{ "%ar" }, /* author date, relative */
|
||||
{ "%at" }, /* author date, UNIX timestamp */
|
||||
{ "%ai" }, /* author date, ISO 8601 */
|
||||
{ "%cn" }, /* committer name */
|
||||
{ "%ce" }, /* committer email */
|
||||
{ "%cd" }, /* committer date */
|
||||
{ "%cD" }, /* committer date, RFC2822 style */
|
||||
{ "%cr" }, /* committer date, relative */
|
||||
{ "%ct" }, /* committer date, UNIX timestamp */
|
||||
{ "%ci" }, /* committer date, ISO 8601 */
|
||||
{ "%e" }, /* encoding */
|
||||
{ "%s" }, /* subject */
|
||||
{ "%b" }, /* body */
|
||||
{ "%Cred" }, /* red */
|
||||
{ "%Cgreen" }, /* green */
|
||||
{ "%Cblue" }, /* blue */
|
||||
{ "%Creset" }, /* reset color */
|
||||
{ "%n" }, /* newline */
|
||||
{ "%m" }, /* left/right/bottom */
|
||||
};
|
||||
enum interp_index {
|
||||
IHASH = 0, IHASH_ABBREV,
|
||||
ITREE, ITREE_ABBREV,
|
||||
IPARENTS, IPARENTS_ABBREV,
|
||||
IAUTHOR_NAME, IAUTHOR_EMAIL,
|
||||
IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE,
|
||||
IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601,
|
||||
ICOMMITTER_NAME, ICOMMITTER_EMAIL,
|
||||
ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822,
|
||||
ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP,
|
||||
ICOMMITTER_ISO8601,
|
||||
IENCODING,
|
||||
ISUBJECT,
|
||||
IBODY,
|
||||
IRED, IGREEN, IBLUE, IRESET_COLOR,
|
||||
INEWLINE,
|
||||
ILEFT_RIGHT,
|
||||
};
|
||||
struct commit_list *p;
|
||||
char parents[1024];
|
||||
unsigned long len;
|
||||
if (chunk->len) {
|
||||
strbuf_adddup(sb, chunk->off, chunk->len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We haven't seen this chunk before. Our caller is surely
|
||||
* going to add it the hard way now. Remember the most likely
|
||||
* start of the to-be-added chunk: the current end of the
|
||||
* struct strbuf.
|
||||
*/
|
||||
chunk->off = sb->len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parse_commit_header(struct format_commit_context *context)
|
||||
{
|
||||
const char *msg = context->commit->buffer;
|
||||
int i;
|
||||
enum { HEADER, SUBJECT, BODY } state;
|
||||
const char *msg = commit->buffer;
|
||||
|
||||
if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
|
||||
die("invalid interp table!");
|
||||
|
||||
/* these are independent of the commit */
|
||||
interp_set_entry(table, IRED, "\033[31m");
|
||||
interp_set_entry(table, IGREEN, "\033[32m");
|
||||
interp_set_entry(table, IBLUE, "\033[34m");
|
||||
interp_set_entry(table, IRESET_COLOR, "\033[m");
|
||||
interp_set_entry(table, INEWLINE, "\n");
|
||||
|
||||
/* these depend on the commit */
|
||||
if (!commit->object.parsed)
|
||||
parse_object(commit->object.sha1);
|
||||
interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1));
|
||||
interp_set_entry(table, IHASH_ABBREV,
|
||||
find_unique_abbrev(commit->object.sha1,
|
||||
DEFAULT_ABBREV));
|
||||
interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1));
|
||||
interp_set_entry(table, ITREE_ABBREV,
|
||||
find_unique_abbrev(commit->tree->object.sha1,
|
||||
DEFAULT_ABBREV));
|
||||
interp_set_entry(table, ILEFT_RIGHT,
|
||||
(commit->object.flags & BOUNDARY)
|
||||
? "-"
|
||||
: (commit->object.flags & SYMMETRIC_LEFT)
|
||||
? "<"
|
||||
: ">");
|
||||
|
||||
parents[1] = 0;
|
||||
for (i = 0, p = commit->parents;
|
||||
p && i < sizeof(parents) - 1;
|
||||
p = p->next)
|
||||
i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
|
||||
sha1_to_hex(p->item->object.sha1));
|
||||
interp_set_entry(table, IPARENTS, parents + 1);
|
||||
|
||||
parents[1] = 0;
|
||||
for (i = 0, p = commit->parents;
|
||||
p && i < sizeof(parents) - 1;
|
||||
p = p->next)
|
||||
i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
|
||||
find_unique_abbrev(p->item->object.sha1,
|
||||
DEFAULT_ABBREV));
|
||||
interp_set_entry(table, IPARENTS_ABBREV, parents + 1);
|
||||
|
||||
for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
|
||||
int eol;
|
||||
@@ -445,7 +405,8 @@ void format_commit_message(const struct commit *commit,
|
||||
; /* do nothing */
|
||||
|
||||
if (state == SUBJECT) {
|
||||
table[ISUBJECT].value = xmemdupz(msg + i, eol - i);
|
||||
context->subject.off = i;
|
||||
context->subject.len = eol - i;
|
||||
i = eol;
|
||||
}
|
||||
if (i == eol) {
|
||||
@@ -453,29 +414,170 @@ void format_commit_message(const struct commit *commit,
|
||||
/* strip empty lines */
|
||||
while (msg[eol + 1] == '\n')
|
||||
eol++;
|
||||
} else if (!prefixcmp(msg + i, "author "))
|
||||
fill_person(table + IAUTHOR_NAME,
|
||||
msg + i + 7, eol - i - 7);
|
||||
else if (!prefixcmp(msg + i, "committer "))
|
||||
fill_person(table + ICOMMITTER_NAME,
|
||||
msg + i + 10, eol - i - 10);
|
||||
else if (!prefixcmp(msg + i, "encoding "))
|
||||
table[IENCODING].value =
|
||||
xmemdupz(msg + i + 9, eol - i - 9);
|
||||
} else if (!prefixcmp(msg + i, "author ")) {
|
||||
context->author.off = i + 7;
|
||||
context->author.len = eol - i - 7;
|
||||
} else if (!prefixcmp(msg + i, "committer ")) {
|
||||
context->committer.off = i + 10;
|
||||
context->committer.len = eol - i - 10;
|
||||
} else if (!prefixcmp(msg + i, "encoding ")) {
|
||||
context->encoding.off = i + 9;
|
||||
context->encoding.len = eol - i - 9;
|
||||
}
|
||||
i = eol;
|
||||
}
|
||||
if (msg[i])
|
||||
table[IBODY].value = xstrdup(msg + i);
|
||||
context->body_off = i;
|
||||
context->commit_header_parsed = 1;
|
||||
}
|
||||
|
||||
len = interpolate(sb->buf + sb->len, strbuf_avail(sb),
|
||||
format, table, ARRAY_SIZE(table));
|
||||
if (len > strbuf_avail(sb)) {
|
||||
strbuf_grow(sb, len);
|
||||
interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1,
|
||||
format, table, ARRAY_SIZE(table));
|
||||
static void format_commit_item(struct strbuf *sb, const char *placeholder,
|
||||
void *context)
|
||||
{
|
||||
struct format_commit_context *c = context;
|
||||
const struct commit *commit = c->commit;
|
||||
const char *msg = commit->buffer;
|
||||
struct commit_list *p;
|
||||
|
||||
/* these are independent of the commit */
|
||||
switch (placeholder[0]) {
|
||||
case 'C':
|
||||
switch (placeholder[3]) {
|
||||
case 'd': /* red */
|
||||
strbuf_addstr(sb, "\033[31m");
|
||||
return;
|
||||
case 'e': /* green */
|
||||
strbuf_addstr(sb, "\033[32m");
|
||||
return;
|
||||
case 'u': /* blue */
|
||||
strbuf_addstr(sb, "\033[34m");
|
||||
return;
|
||||
case 's': /* reset color */
|
||||
strbuf_addstr(sb, "\033[m");
|
||||
return;
|
||||
}
|
||||
case 'n': /* newline */
|
||||
strbuf_addch(sb, '\n');
|
||||
return;
|
||||
}
|
||||
strbuf_setlen(sb, sb->len + len);
|
||||
interp_clear_table(table, ARRAY_SIZE(table));
|
||||
|
||||
/* these depend on the commit */
|
||||
if (!commit->object.parsed)
|
||||
parse_object(commit->object.sha1);
|
||||
|
||||
switch (placeholder[0]) {
|
||||
case 'H': /* commit hash */
|
||||
strbuf_addstr(sb, sha1_to_hex(commit->object.sha1));
|
||||
return;
|
||||
case 'h': /* abbreviated commit hash */
|
||||
if (add_again(sb, &c->abbrev_commit_hash))
|
||||
return;
|
||||
strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
|
||||
DEFAULT_ABBREV));
|
||||
c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
|
||||
return;
|
||||
case 'T': /* tree hash */
|
||||
strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1));
|
||||
return;
|
||||
case 't': /* abbreviated tree hash */
|
||||
if (add_again(sb, &c->abbrev_tree_hash))
|
||||
return;
|
||||
strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
|
||||
DEFAULT_ABBREV));
|
||||
c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
|
||||
return;
|
||||
case 'P': /* parent hashes */
|
||||
for (p = commit->parents; p; p = p->next) {
|
||||
if (p != commit->parents)
|
||||
strbuf_addch(sb, ' ');
|
||||
strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1));
|
||||
}
|
||||
return;
|
||||
case 'p': /* abbreviated parent hashes */
|
||||
if (add_again(sb, &c->abbrev_parent_hashes))
|
||||
return;
|
||||
for (p = commit->parents; p; p = p->next) {
|
||||
if (p != commit->parents)
|
||||
strbuf_addch(sb, ' ');
|
||||
strbuf_addstr(sb, find_unique_abbrev(
|
||||
p->item->object.sha1, DEFAULT_ABBREV));
|
||||
}
|
||||
c->abbrev_parent_hashes.len = sb->len -
|
||||
c->abbrev_parent_hashes.off;
|
||||
return;
|
||||
case 'm': /* left/right/bottom */
|
||||
strbuf_addch(sb, (commit->object.flags & BOUNDARY)
|
||||
? '-'
|
||||
: (commit->object.flags & SYMMETRIC_LEFT)
|
||||
? '<'
|
||||
: '>');
|
||||
return;
|
||||
}
|
||||
|
||||
/* For the rest we have to parse the commit header. */
|
||||
if (!c->commit_header_parsed)
|
||||
parse_commit_header(c);
|
||||
|
||||
switch (placeholder[0]) {
|
||||
case 's':
|
||||
strbuf_add(sb, msg + c->subject.off, c->subject.len);
|
||||
return;
|
||||
case 'a':
|
||||
format_person_part(sb, placeholder[1],
|
||||
msg + c->author.off, c->author.len);
|
||||
return;
|
||||
case 'c':
|
||||
format_person_part(sb, placeholder[1],
|
||||
msg + c->committer.off, c->committer.len);
|
||||
return;
|
||||
case 'e':
|
||||
strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
|
||||
return;
|
||||
case 'b':
|
||||
strbuf_addstr(sb, msg + c->body_off);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void format_commit_message(const struct commit *commit,
|
||||
const void *format, struct strbuf *sb)
|
||||
{
|
||||
const char *placeholders[] = {
|
||||
"H", /* commit hash */
|
||||
"h", /* abbreviated commit hash */
|
||||
"T", /* tree hash */
|
||||
"t", /* abbreviated tree hash */
|
||||
"P", /* parent hashes */
|
||||
"p", /* abbreviated parent hashes */
|
||||
"an", /* author name */
|
||||
"ae", /* author email */
|
||||
"ad", /* author date */
|
||||
"aD", /* author date, RFC2822 style */
|
||||
"ar", /* author date, relative */
|
||||
"at", /* author date, UNIX timestamp */
|
||||
"ai", /* author date, ISO 8601 */
|
||||
"cn", /* committer name */
|
||||
"ce", /* committer email */
|
||||
"cd", /* committer date */
|
||||
"cD", /* committer date, RFC2822 style */
|
||||
"cr", /* committer date, relative */
|
||||
"ct", /* committer date, UNIX timestamp */
|
||||
"ci", /* committer date, ISO 8601 */
|
||||
"e", /* encoding */
|
||||
"s", /* subject */
|
||||
"b", /* body */
|
||||
"Cred", /* red */
|
||||
"Cgreen", /* green */
|
||||
"Cblue", /* blue */
|
||||
"Creset", /* reset color */
|
||||
"n", /* newline */
|
||||
"m", /* left/right/bottom */
|
||||
NULL
|
||||
};
|
||||
struct format_commit_context context;
|
||||
|
||||
memset(&context, 0, sizeof(context));
|
||||
context.commit = commit;
|
||||
strbuf_expand(sb, format, placeholders, format_commit_item, &context);
|
||||
}
|
||||
|
||||
static void pp_header(enum cmit_fmt fmt,
|
||||
|
||||
101
progress.c
101
progress.c
@@ -14,12 +14,12 @@
|
||||
#define TP_IDX_MAX 8
|
||||
|
||||
struct throughput {
|
||||
off_t curr_total;
|
||||
off_t prev_total;
|
||||
struct timeval prev_tv;
|
||||
off_t total;
|
||||
unsigned long count;
|
||||
unsigned long avg_bytes;
|
||||
unsigned long last_bytes[TP_IDX_MAX];
|
||||
unsigned int avg_bytes;
|
||||
unsigned int avg_misecs;
|
||||
unsigned int last_bytes[TP_IDX_MAX];
|
||||
unsigned int last_misecs[TP_IDX_MAX];
|
||||
unsigned int idx;
|
||||
char display[32];
|
||||
@@ -69,9 +69,9 @@ static void clear_progress_signal(void)
|
||||
progress_update = 0;
|
||||
}
|
||||
|
||||
static int display(struct progress *progress, unsigned n, int done)
|
||||
static int display(struct progress *progress, unsigned n, const char *done)
|
||||
{
|
||||
char *eol, *tp;
|
||||
const char *eol, *tp;
|
||||
|
||||
if (progress->delay) {
|
||||
if (!progress_update || --progress->delay)
|
||||
@@ -90,7 +90,7 @@ static int display(struct progress *progress, unsigned n, int done)
|
||||
|
||||
progress->last_value = n;
|
||||
tp = (progress->throughput) ? progress->throughput->display : "";
|
||||
eol = done ? ", done. \n" : " \r";
|
||||
eol = done ? done : " \r";
|
||||
if (progress->total) {
|
||||
unsigned percent = n * 100 / progress->total;
|
||||
if (percent != progress->last_percent || progress_update) {
|
||||
@@ -110,7 +110,31 @@ static int display(struct progress *progress, unsigned n, int done)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void display_throughput(struct progress *progress, unsigned long n)
|
||||
static void throughput_string(struct throughput *tp, off_t total,
|
||||
unsigned int rate)
|
||||
{
|
||||
int l = sizeof(tp->display);
|
||||
if (total > 1 << 30) {
|
||||
l -= snprintf(tp->display, l, ", %u.%2.2u GiB",
|
||||
(int)(total >> 30),
|
||||
(int)(total & ((1 << 30) - 1)) / 10737419);
|
||||
} else if (total > 1 << 20) {
|
||||
l -= snprintf(tp->display, l, ", %u.%2.2u MiB",
|
||||
(int)(total >> 20),
|
||||
((int)(total & ((1 << 20) - 1)) * 100) >> 20);
|
||||
} else if (total > 1 << 10) {
|
||||
l -= snprintf(tp->display, l, ", %u.%2.2u KiB",
|
||||
(int)(total >> 10),
|
||||
((int)(total & ((1 << 10) - 1)) * 100) >> 10);
|
||||
} else {
|
||||
l -= snprintf(tp->display, l, ", %u bytes", (int)total);
|
||||
}
|
||||
if (rate)
|
||||
snprintf(tp->display + sizeof(tp->display) - l, l,
|
||||
" | %u KiB/s", rate);
|
||||
}
|
||||
|
||||
void display_throughput(struct progress *progress, off_t total)
|
||||
{
|
||||
struct throughput *tp;
|
||||
struct timeval tv;
|
||||
@@ -124,13 +148,13 @@ void display_throughput(struct progress *progress, unsigned long n)
|
||||
|
||||
if (!tp) {
|
||||
progress->throughput = tp = calloc(1, sizeof(*tp));
|
||||
if (tp)
|
||||
if (tp) {
|
||||
tp->prev_total = tp->curr_total = total;
|
||||
tp->prev_tv = tv;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
tp->total += n;
|
||||
tp->count += n;
|
||||
tp->curr_total = total;
|
||||
|
||||
/*
|
||||
* We have x = bytes and y = microsecs. We want z = KiB/s:
|
||||
@@ -151,47 +175,29 @@ void display_throughput(struct progress *progress, unsigned long n)
|
||||
misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977;
|
||||
|
||||
if (misecs > 512) {
|
||||
int l = sizeof(tp->display);
|
||||
unsigned int count, rate;
|
||||
|
||||
count = total - tp->prev_total;
|
||||
tp->prev_total = total;
|
||||
tp->prev_tv = tv;
|
||||
tp->avg_bytes += tp->count;
|
||||
tp->avg_bytes += count;
|
||||
tp->avg_misecs += misecs;
|
||||
|
||||
if (tp->total > 1 << 30) {
|
||||
l -= snprintf(tp->display, l, ", %u.%2.2u GiB",
|
||||
(int)(tp->total >> 30),
|
||||
(int)(tp->total & ((1 << 30) - 1)) / 10737419);
|
||||
} else if (tp->total > 1 << 20) {
|
||||
l -= snprintf(tp->display, l, ", %u.%2.2u MiB",
|
||||
(int)(tp->total >> 20),
|
||||
((int)(tp->total & ((1 << 20) - 1))
|
||||
* 100) >> 20);
|
||||
} else if (tp->total > 1 << 10) {
|
||||
l -= snprintf(tp->display, l, ", %u.%2.2u KiB",
|
||||
(int)(tp->total >> 10),
|
||||
((int)(tp->total & ((1 << 10) - 1))
|
||||
* 100) >> 10);
|
||||
} else {
|
||||
l -= snprintf(tp->display, l, ", %u bytes",
|
||||
(int)tp->total);
|
||||
}
|
||||
snprintf(tp->display + sizeof(tp->display) - l, l,
|
||||
" | %lu KiB/s", tp->avg_bytes / tp->avg_misecs);
|
||||
|
||||
rate = tp->avg_bytes / tp->avg_misecs;
|
||||
tp->avg_bytes -= tp->last_bytes[tp->idx];
|
||||
tp->avg_misecs -= tp->last_misecs[tp->idx];
|
||||
tp->last_bytes[tp->idx] = tp->count;
|
||||
tp->last_bytes[tp->idx] = count;
|
||||
tp->last_misecs[tp->idx] = misecs;
|
||||
tp->idx = (tp->idx + 1) % TP_IDX_MAX;
|
||||
tp->count = 0;
|
||||
|
||||
throughput_string(tp, total, rate);
|
||||
if (progress->last_value != -1 && progress_update)
|
||||
display(progress, progress->last_value, 0);
|
||||
display(progress, progress->last_value, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int display_progress(struct progress *progress, unsigned n)
|
||||
{
|
||||
return progress ? display(progress, n, 0) : 0;
|
||||
return progress ? display(progress, n, NULL) : 0;
|
||||
}
|
||||
|
||||
struct progress *start_progress_delay(const char *title, unsigned total,
|
||||
@@ -220,6 +226,11 @@ struct progress *start_progress(const char *title, unsigned total)
|
||||
}
|
||||
|
||||
void stop_progress(struct progress **p_progress)
|
||||
{
|
||||
stop_progress_msg(p_progress, "done");
|
||||
}
|
||||
|
||||
void stop_progress_msg(struct progress **p_progress, const char *msg)
|
||||
{
|
||||
struct progress *progress = *p_progress;
|
||||
if (!progress)
|
||||
@@ -227,8 +238,16 @@ void stop_progress(struct progress **p_progress)
|
||||
*p_progress = NULL;
|
||||
if (progress->last_value != -1) {
|
||||
/* Force the last update */
|
||||
char buf[strlen(msg) + 5];
|
||||
struct throughput *tp = progress->throughput;
|
||||
if (tp) {
|
||||
unsigned int rate = !tp->avg_misecs ? 0 :
|
||||
tp->avg_bytes / tp->avg_misecs;
|
||||
throughput_string(tp, tp->curr_total, rate);
|
||||
}
|
||||
progress_update = 1;
|
||||
display(progress, progress->last_value, 1);
|
||||
sprintf(buf, ", %s.\n", msg);
|
||||
display(progress, progress->last_value, buf);
|
||||
}
|
||||
clear_progress_signal();
|
||||
free(progress->throughput);
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
|
||||
struct progress;
|
||||
|
||||
void display_throughput(struct progress *progress, unsigned long n);
|
||||
void display_throughput(struct progress *progress, off_t total);
|
||||
int display_progress(struct progress *progress, unsigned n);
|
||||
struct progress *start_progress(const char *title, unsigned total);
|
||||
struct progress *start_progress_delay(const char *title, unsigned total,
|
||||
unsigned percent_treshold, unsigned delay);
|
||||
void stop_progress(struct progress **progress);
|
||||
void stop_progress_msg(struct progress **progress, const char *msg);
|
||||
|
||||
#endif
|
||||
|
||||
48
read-cache.c
48
read-cache.c
@@ -194,11 +194,12 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
|
||||
}
|
||||
|
||||
int ie_match_stat(struct index_state *istate,
|
||||
struct cache_entry *ce, struct stat *st, int options)
|
||||
struct cache_entry *ce, struct stat *st,
|
||||
unsigned int options)
|
||||
{
|
||||
unsigned int changed;
|
||||
int ignore_valid = options & 01;
|
||||
int assume_racy_is_modified = options & 02;
|
||||
int ignore_valid = options & CE_MATCH_IGNORE_VALID;
|
||||
int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY;
|
||||
|
||||
/*
|
||||
* If it's marked as always valid in the index, it's
|
||||
@@ -238,10 +239,11 @@ int ie_match_stat(struct index_state *istate,
|
||||
}
|
||||
|
||||
int ie_modified(struct index_state *istate,
|
||||
struct cache_entry *ce, struct stat *st, int really)
|
||||
struct cache_entry *ce, struct stat *st, unsigned int options)
|
||||
{
|
||||
int changed, changed_fs;
|
||||
changed = ie_match_stat(istate, ce, st, really);
|
||||
|
||||
changed = ie_match_stat(istate, ce, st, options);
|
||||
if (!changed)
|
||||
return 0;
|
||||
/*
|
||||
@@ -387,6 +389,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
|
||||
int size, namelen, pos;
|
||||
struct stat st;
|
||||
struct cache_entry *ce;
|
||||
unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
|
||||
|
||||
if (lstat(path, &st))
|
||||
die("%s: unable to stat (%s)", path, strerror(errno));
|
||||
@@ -421,7 +424,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
|
||||
pos = index_name_pos(istate, ce->name, namelen);
|
||||
if (0 <= pos &&
|
||||
!ce_stage(istate->cache[pos]) &&
|
||||
!ie_modified(istate, istate->cache[pos], &st, 1)) {
|
||||
!ie_match_stat(istate, istate->cache[pos], &st, ce_option)) {
|
||||
/* Nothing changed, really */
|
||||
free(ce);
|
||||
return 0;
|
||||
@@ -783,11 +786,13 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
|
||||
* to link up the stat cache details with the proper files.
|
||||
*/
|
||||
static struct cache_entry *refresh_cache_ent(struct index_state *istate,
|
||||
struct cache_entry *ce, int really, int *err)
|
||||
struct cache_entry *ce,
|
||||
unsigned int options, int *err)
|
||||
{
|
||||
struct stat st;
|
||||
struct cache_entry *updated;
|
||||
int changed, size;
|
||||
int ignore_valid = options & CE_MATCH_IGNORE_VALID;
|
||||
|
||||
if (lstat(ce->name, &st) < 0) {
|
||||
if (err)
|
||||
@@ -795,16 +800,23 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
changed = ie_match_stat(istate, ce, &st, really);
|
||||
changed = ie_match_stat(istate, ce, &st, options);
|
||||
if (!changed) {
|
||||
if (really && assume_unchanged &&
|
||||
/*
|
||||
* The path is unchanged. If we were told to ignore
|
||||
* valid bit, then we did the actual stat check and
|
||||
* found that the entry is unmodified. If the entry
|
||||
* is not marked VALID, this is the place to mark it
|
||||
* valid again, under "assume unchanged" mode.
|
||||
*/
|
||||
if (ignore_valid && assume_unchanged &&
|
||||
!(ce->ce_flags & htons(CE_VALID)))
|
||||
; /* mark this one VALID again */
|
||||
else
|
||||
return ce;
|
||||
}
|
||||
|
||||
if (ie_modified(istate, ce, &st, really)) {
|
||||
if (ie_modified(istate, ce, &st, options)) {
|
||||
if (err)
|
||||
*err = EINVAL;
|
||||
return NULL;
|
||||
@@ -815,13 +827,14 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
|
||||
memcpy(updated, ce, size);
|
||||
fill_stat_cache_info(updated, &st);
|
||||
|
||||
/* In this case, if really is not set, we should leave
|
||||
* CE_VALID bit alone. Otherwise, paths marked with
|
||||
* --no-assume-unchanged (i.e. things to be edited) will
|
||||
* reacquire CE_VALID bit automatically, which is not
|
||||
* really what we want.
|
||||
/*
|
||||
* If ignore_valid is not set, we should leave CE_VALID bit
|
||||
* alone. Otherwise, paths marked with --no-assume-unchanged
|
||||
* (i.e. things to be edited) will reacquire CE_VALID bit
|
||||
* automatically, which is not really what we want.
|
||||
*/
|
||||
if (!really && assume_unchanged && !(ce->ce_flags & htons(CE_VALID)))
|
||||
if (!ignore_valid && assume_unchanged &&
|
||||
!(ce->ce_flags & htons(CE_VALID)))
|
||||
updated->ce_flags &= ~htons(CE_VALID);
|
||||
|
||||
return updated;
|
||||
@@ -835,6 +848,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
|
||||
int allow_unmerged = (flags & REFRESH_UNMERGED) != 0;
|
||||
int quiet = (flags & REFRESH_QUIET) != 0;
|
||||
int not_new = (flags & REFRESH_IGNORE_MISSING) != 0;
|
||||
unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0;
|
||||
|
||||
for (i = 0; i < istate->cache_nr; i++) {
|
||||
struct cache_entry *ce, *new;
|
||||
@@ -856,7 +870,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
|
||||
if (pathspec && !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen))
|
||||
continue;
|
||||
|
||||
new = refresh_cache_ent(istate, ce, really, &cache_errno);
|
||||
new = refresh_cache_ent(istate, ce, options, &cache_errno);
|
||||
if (new == ce)
|
||||
continue;
|
||||
if (!new) {
|
||||
|
||||
@@ -38,7 +38,7 @@ int start_command(struct child_process *cmd)
|
||||
cmd->close_out = 1;
|
||||
}
|
||||
|
||||
need_err = cmd->err < 0;
|
||||
need_err = !cmd->no_stderr && cmd->err < 0;
|
||||
if (need_err) {
|
||||
if (pipe(fderr) < 0) {
|
||||
if (need_in)
|
||||
@@ -69,8 +69,10 @@ int start_command(struct child_process *cmd)
|
||||
fdout[1] = cmd->out;
|
||||
}
|
||||
|
||||
if (need_err) {
|
||||
fderr[1] = cmd->err;
|
||||
if (cmd->no_stderr)
|
||||
fderr[1] = open("/dev/null", O_RDWR);
|
||||
else if (need_err) {
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
if (cmd->dir)
|
||||
|
||||
@@ -23,6 +23,7 @@ struct child_process {
|
||||
unsigned close_out:1;
|
||||
unsigned no_stdin:1;
|
||||
unsigned no_stdout:1;
|
||||
unsigned no_stderr:1;
|
||||
unsigned git_cmd:1; /* if this is to be git sub-command */
|
||||
unsigned stdout_to_stderr:1;
|
||||
};
|
||||
|
||||
51
sideband.c
51
sideband.c
@@ -11,13 +11,19 @@
|
||||
* stream, aka "verbose"). A message over band #3 is a signal that
|
||||
* the remote died unexpectedly. A flush() concludes the stream.
|
||||
*/
|
||||
|
||||
#define PREFIX "remote:"
|
||||
#define SUFFIX "\033[K" /* change to " " if ANSI sequences don't work */
|
||||
|
||||
int recv_sideband(const char *me, int in_stream, int out, int err)
|
||||
{
|
||||
char buf[7 + LARGE_PACKET_MAX + 1];
|
||||
strcpy(buf, "remote:");
|
||||
unsigned pf = strlen(PREFIX);
|
||||
unsigned sf = strlen(SUFFIX);
|
||||
char buf[pf + LARGE_PACKET_MAX + sf + 1];
|
||||
memcpy(buf, PREFIX, pf);
|
||||
while (1) {
|
||||
int band, len;
|
||||
len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX);
|
||||
len = packet_read_line(in_stream, buf + pf, LARGE_PACKET_MAX);
|
||||
if (len == 0)
|
||||
break;
|
||||
if (len < 1) {
|
||||
@@ -25,35 +31,52 @@ int recv_sideband(const char *me, int in_stream, int out, int err)
|
||||
safe_write(err, buf, len);
|
||||
return SIDEBAND_PROTOCOL_ERROR;
|
||||
}
|
||||
band = buf[7] & 0xff;
|
||||
band = buf[pf] & 0xff;
|
||||
len--;
|
||||
switch (band) {
|
||||
case 3:
|
||||
buf[7] = ' ';
|
||||
buf[8+len] = '\n';
|
||||
safe_write(err, buf, 8+len+1);
|
||||
buf[pf] = ' ';
|
||||
buf[pf+1+len] = '\n';
|
||||
safe_write(err, buf, pf+1+len+1);
|
||||
return SIDEBAND_REMOTE_ERROR;
|
||||
case 2:
|
||||
buf[7] = ' ';
|
||||
len += 8;
|
||||
buf[pf] = ' ';
|
||||
len += pf+1;
|
||||
while (1) {
|
||||
int brk = 8;
|
||||
int brk = pf+1;
|
||||
|
||||
/* Break the buffer into separate lines. */
|
||||
while (brk < len) {
|
||||
brk++;
|
||||
if (buf[brk-1] == '\n' ||
|
||||
buf[brk-1] == '\r')
|
||||
break;
|
||||
}
|
||||
safe_write(err, buf, brk);
|
||||
|
||||
/*
|
||||
* Let's insert a suffix to clear the end
|
||||
* of the screen line, but only if current
|
||||
* line data actually contains something.
|
||||
*/
|
||||
if (brk > pf+1 + 1) {
|
||||
char save[sf];
|
||||
memcpy(save, buf + brk, sf);
|
||||
buf[brk + sf - 1] = buf[brk - 1];
|
||||
memcpy(buf + brk - 1, SUFFIX, sf);
|
||||
safe_write(err, buf, brk + sf);
|
||||
memcpy(buf + brk, save, sf);
|
||||
} else
|
||||
safe_write(err, buf, brk);
|
||||
|
||||
if (brk < len) {
|
||||
memmove(buf + 8, buf + brk, len - brk);
|
||||
len = len - brk + 8;
|
||||
memmove(buf + pf+1, buf + brk, len - brk);
|
||||
len = len - brk + pf+1;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case 1:
|
||||
safe_write(out, buf+8, len);
|
||||
safe_write(out, buf + pf+1, len);
|
||||
continue;
|
||||
default:
|
||||
len = sprintf(buf,
|
||||
|
||||
38
strbuf.c
38
strbuf.c
@@ -106,17 +106,25 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len)
|
||||
strbuf_setlen(sb, sb->len + len);
|
||||
}
|
||||
|
||||
void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len)
|
||||
{
|
||||
strbuf_grow(sb, len);
|
||||
memcpy(sb->buf + sb->len, sb->buf + pos, len);
|
||||
strbuf_setlen(sb, sb->len + len);
|
||||
}
|
||||
|
||||
void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
|
||||
{
|
||||
int len;
|
||||
va_list ap;
|
||||
|
||||
if (!strbuf_avail(sb))
|
||||
strbuf_grow(sb, 64);
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
|
||||
va_end(ap);
|
||||
if (len < 0) {
|
||||
len = 0;
|
||||
}
|
||||
if (len < 0)
|
||||
die("your vsnprintf is broken");
|
||||
if (len > strbuf_avail(sb)) {
|
||||
strbuf_grow(sb, len);
|
||||
va_start(ap, fmt);
|
||||
@@ -129,6 +137,30 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
|
||||
strbuf_setlen(sb, sb->len + len);
|
||||
}
|
||||
|
||||
void strbuf_expand(struct strbuf *sb, const char *format,
|
||||
const char **placeholders, expand_fn_t fn, void *context)
|
||||
{
|
||||
for (;;) {
|
||||
const char *percent, **p;
|
||||
|
||||
percent = strchrnul(format, '%');
|
||||
strbuf_add(sb, format, percent - format);
|
||||
if (!*percent)
|
||||
break;
|
||||
format = percent + 1;
|
||||
|
||||
for (p = placeholders; *p; p++) {
|
||||
if (!prefixcmp(format, *p))
|
||||
break;
|
||||
}
|
||||
if (*p) {
|
||||
fn(sb, *p, context);
|
||||
format += strlen(*p);
|
||||
} else
|
||||
strbuf_addch(sb, '%');
|
||||
}
|
||||
}
|
||||
|
||||
size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
|
||||
{
|
||||
size_t res;
|
||||
|
||||
4
strbuf.h
4
strbuf.h
@@ -101,6 +101,10 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s) {
|
||||
static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) {
|
||||
strbuf_add(sb, sb2->buf, sb2->len);
|
||||
}
|
||||
extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
|
||||
|
||||
typedef void (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
|
||||
extern void strbuf_expand(struct strbuf *sb, const char *format, const char **placeholders, expand_fn_t fn, void *context);
|
||||
|
||||
__attribute__((format(printf,2,3)))
|
||||
extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
|
||||
|
||||
@@ -18,6 +18,7 @@ string options
|
||||
-s, --string <string>
|
||||
get a string
|
||||
--string2 <str> get another string
|
||||
--st <st> get another string (pervert ordering)
|
||||
|
||||
EOF
|
||||
|
||||
@@ -90,4 +91,16 @@ test_expect_failure 'ambiguously abbreviated option' '
|
||||
test $? != 129
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
boolean: 0
|
||||
integer: 0
|
||||
string: 123
|
||||
EOF
|
||||
|
||||
test_expect_success 'non ambiguous option (after two options it abbreviates)' '
|
||||
test-parse-options --st 123 > output 2> output.err &&
|
||||
test ! -s output.err &&
|
||||
git diff expect output
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='git add -u with path limiting
|
||||
test_description='git add -u
|
||||
|
||||
This test creates a working tree state with three files:
|
||||
|
||||
@@ -9,7 +9,10 @@ This test creates a working tree state with three files:
|
||||
dir/other (untracked)
|
||||
|
||||
and issues a git add -u with path limiting on "dir" to add
|
||||
only the updates to dir/sub.'
|
||||
only the updates to dir/sub.
|
||||
|
||||
Also tested are "git add -u" without limiting, and "git add -u"
|
||||
without contents changes.'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
@@ -85,4 +88,27 @@ test_expect_success 'replace a file with a symlink' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'add everything changed' '
|
||||
|
||||
git add -u &&
|
||||
test -z "$(git diff-files)"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'touch and then add -u' '
|
||||
|
||||
touch check &&
|
||||
git add -u &&
|
||||
test -z "$(git diff-files)"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'touch and then add explicitly' '
|
||||
|
||||
touch check &&
|
||||
git add check &&
|
||||
test -z "$(git diff-files)"
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -48,9 +48,14 @@ test_expect_success 'reference merge' '
|
||||
git merge -s recursive "reference merge" HEAD master
|
||||
'
|
||||
|
||||
PRE_REBASE=$(git rev-parse test-rebase)
|
||||
test_expect_success rebase '
|
||||
git checkout test-rebase &&
|
||||
git rebase --merge master
|
||||
GIT_TRACE=1 git rebase --merge master
|
||||
'
|
||||
|
||||
test_expect_success 'test-rebase@{1} is pre rebase' '
|
||||
test $PRE_REBASE = $(git rev-parse test-rebase@{1})
|
||||
'
|
||||
|
||||
test_expect_success 'merge and rebase should match' '
|
||||
|
||||
@@ -39,6 +39,19 @@ test_expect_success 'rebase --skip with am -3' '
|
||||
git reset --hard HEAD &&
|
||||
git rebase --skip
|
||||
'
|
||||
|
||||
test_expect_success 'rebase moves back to skip-reference' '
|
||||
test refs/heads/skip-reference = $(git symbolic-ref HEAD) &&
|
||||
git branch post-rebase &&
|
||||
git reset --hard pre-rebase &&
|
||||
! git rebase master &&
|
||||
echo "hello" > hello &&
|
||||
git add hello &&
|
||||
git rebase --continue &&
|
||||
test refs/heads/skip-reference = $(git symbolic-ref HEAD) &&
|
||||
git reset --hard post-rebase
|
||||
'
|
||||
|
||||
test_expect_success 'checkout skip-merge' 'git checkout -f skip-merge'
|
||||
|
||||
test_expect_failure 'rebase with --merge' 'git rebase --merge master'
|
||||
@@ -51,6 +64,10 @@ test_expect_success 'rebase --skip with --merge' '
|
||||
test_expect_success 'merge and reference trees equal' \
|
||||
'test -z "`git diff-tree skip-merge skip-reference`"'
|
||||
|
||||
test_expect_success 'moved back to branch correctly' '
|
||||
test refs/heads/skip-merge = $(git symbolic-ref HEAD)
|
||||
'
|
||||
|
||||
test_debug 'gitk --all & sleep 1'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -149,7 +149,7 @@ test_expect_success 'stop on conflicting pick' '
|
||||
diff -u expect .git/.dotest-merge/patch &&
|
||||
diff -u expect2 file1 &&
|
||||
test 4 = $(grep -v "^#" < .git/.dotest-merge/done | wc -l) &&
|
||||
test 0 = $(grep -v "^#" < .git/.dotest-merge/todo | wc -l)
|
||||
test 0 = $(grep -v "^#" < .git/.dotest-merge/git-rebase-todo | wc -l)
|
||||
'
|
||||
|
||||
test_expect_success 'abort' '
|
||||
|
||||
106
t/t4021-format-patch-numbered.sh
Executable file
106
t/t4021-format-patch-numbered.sh
Executable file
@@ -0,0 +1,106 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2006 Brian C Gernhardt
|
||||
#
|
||||
|
||||
test_description='Format-patch numbering options'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success setup '
|
||||
|
||||
echo A > file &&
|
||||
git add file &&
|
||||
git commit -m First &&
|
||||
|
||||
echo B >> file &&
|
||||
git commit -a -m Second &&
|
||||
|
||||
echo C >> file &&
|
||||
git commit -a -m Third
|
||||
|
||||
'
|
||||
|
||||
# Each of these gets used multiple times.
|
||||
|
||||
test_num_no_numbered() {
|
||||
cnt=$(grep "^Subject: \[PATCH\]" $1 | wc -l) &&
|
||||
test $cnt = $2
|
||||
}
|
||||
|
||||
test_single_no_numbered() {
|
||||
test_num_no_numbered $1 1
|
||||
}
|
||||
|
||||
test_no_numbered() {
|
||||
test_num_no_numbered $1 2
|
||||
}
|
||||
|
||||
test_single_numbered() {
|
||||
grep "^Subject: \[PATCH 1/1\]" $1
|
||||
}
|
||||
|
||||
test_numbered() {
|
||||
grep "^Subject: \[PATCH 1/2\]" $1 &&
|
||||
grep "^Subject: \[PATCH 2/2\]" $1
|
||||
}
|
||||
|
||||
test_expect_success 'Default: no numbered' '
|
||||
|
||||
git format-patch --stdout HEAD~2 >patch0 &&
|
||||
test_no_numbered patch0
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'Use --numbered' '
|
||||
|
||||
git format-patch --numbered --stdout HEAD~2 >patch1 &&
|
||||
test_numbered patch1
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'format.numbered = true' '
|
||||
|
||||
git config format.numbered true &&
|
||||
git format-patch --stdout HEAD~2 >patch2 &&
|
||||
test_numbered patch2
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'format.numbered && single patch' '
|
||||
|
||||
git format-patch --stdout HEAD^ > patch3 &&
|
||||
test_single_numbered patch3
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'format.numbered && --no-numbered' '
|
||||
|
||||
git format-patch --no-numbered --stdout HEAD~2 >patch4 &&
|
||||
test_no_numbered patch4
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'format.numbered = auto' '
|
||||
|
||||
git config format.numbered auto
|
||||
git format-patch --stdout HEAD~2 > patch5 &&
|
||||
test_numbered patch5
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'format.numbered = auto && single patch' '
|
||||
|
||||
git format-patch --stdout HEAD^ > patch6 &&
|
||||
test_single_no_numbered patch6
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'format.numbered = auto && --no-numbered' '
|
||||
|
||||
git format-patch --no-numbered --stdout HEAD~2 > patch7 &&
|
||||
test_no_numbered patch7
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -86,4 +86,37 @@ test_expect_success 'quickfetch should not leave a corrupted repository' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'quickfetch should not copy from alternate' '
|
||||
|
||||
(
|
||||
mkdir quickclone &&
|
||||
cd quickclone &&
|
||||
git init-db &&
|
||||
(cd ../.git/objects && pwd) >.git/objects/info/alternates &&
|
||||
git remote add origin .. &&
|
||||
git fetch -k -k
|
||||
) &&
|
||||
obj_cnt=$( (
|
||||
cd quickclone &&
|
||||
git count-objects | sed -e "s/ *objects,.*//"
|
||||
) ) &&
|
||||
pck_cnt=$( (
|
||||
cd quickclone &&
|
||||
git count-objects -v | sed -n -e "/packs:/{
|
||||
s/packs://
|
||||
p
|
||||
q
|
||||
}"
|
||||
) ) &&
|
||||
origin_master=$( (
|
||||
cd quickclone &&
|
||||
git rev-parse origin/master
|
||||
) ) &&
|
||||
echo "loose objects: $obj_cnt, packfiles: $pck_cnt" &&
|
||||
test $obj_cnt -eq 0 &&
|
||||
test $pck_cnt -eq 0 &&
|
||||
test z$origin_master = z$(git rev-parse master)
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
75
t/t5530-upload-pack-error.sh
Executable file
75
t/t5530-upload-pack-error.sh
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='errors in upload-pack'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
D=`pwd`
|
||||
|
||||
corrupt_repo () {
|
||||
object_sha1=$(git rev-parse "$1") &&
|
||||
ob=$(expr "$object_sha1" : "\(..\)") &&
|
||||
ject=$(expr "$object_sha1" : "..\(..*\)") &&
|
||||
rm -f ".git/objects/$ob/$ject"
|
||||
}
|
||||
|
||||
test_expect_success 'setup and corrupt repository' '
|
||||
|
||||
echo file >file &&
|
||||
git add file &&
|
||||
git rev-parse :file &&
|
||||
git commit -a -m original &&
|
||||
test_tick &&
|
||||
echo changed >file &&
|
||||
git commit -a -m changed &&
|
||||
corrupt_repo HEAD:file
|
||||
|
||||
'
|
||||
|
||||
test_expect_failure 'fsck fails' '
|
||||
|
||||
git fsck
|
||||
'
|
||||
|
||||
test_expect_success 'upload-pack fails due to error in pack-objects' '
|
||||
|
||||
! echo "0032want $(git rev-parse HEAD)
|
||||
00000009done
|
||||
0000" | git-upload-pack . > /dev/null 2> output.err &&
|
||||
grep "pack-objects died" output.err
|
||||
'
|
||||
|
||||
test_expect_success 'corrupt repo differently' '
|
||||
|
||||
git hash-object -w file &&
|
||||
corrupt_repo HEAD^^{tree}
|
||||
|
||||
'
|
||||
|
||||
test_expect_failure 'fsck fails' '
|
||||
|
||||
git fsck
|
||||
'
|
||||
test_expect_success 'upload-pack fails due to error in rev-list' '
|
||||
|
||||
! echo "0032want $(git rev-parse HEAD)
|
||||
00000009done
|
||||
0000" | git-upload-pack . > /dev/null 2> output.err &&
|
||||
grep "waitpid (async) failed" output.err
|
||||
'
|
||||
|
||||
test_expect_success 'create empty repository' '
|
||||
|
||||
mkdir foo &&
|
||||
cd foo &&
|
||||
git init
|
||||
|
||||
'
|
||||
|
||||
test_expect_failure 'fetch fails' '
|
||||
|
||||
git fetch .. master
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
@@ -148,4 +148,26 @@ test_expect_success 'Check format "rfc2822" date fields output' '
|
||||
git diff expected actual
|
||||
'
|
||||
|
||||
cat >expected <<\EOF
|
||||
refs/heads/master
|
||||
refs/tags/testtag
|
||||
EOF
|
||||
|
||||
test_expect_success 'Verify ascending sort' '
|
||||
git-for-each-ref --format="%(refname)" --sort=refname >actual &&
|
||||
git diff expected actual
|
||||
'
|
||||
|
||||
|
||||
cat >expected <<\EOF
|
||||
refs/tags/testtag
|
||||
refs/heads/master
|
||||
EOF
|
||||
|
||||
test_expect_success 'Verify descending sort' '
|
||||
git-for-each-ref --format="%(refname)" --sort=-refname >actual &&
|
||||
git diff expected actual
|
||||
'
|
||||
|
||||
|
||||
test_done
|
||||
|
||||
@@ -1005,4 +1005,20 @@ test_expect_failure \
|
||||
'verify signed tag fails when public key is not present' \
|
||||
'git-tag -v signed-tag'
|
||||
|
||||
test_expect_success \
|
||||
'message in editor has initial comment' '
|
||||
GIT_EDITOR=cat git tag -a initial-comment > actual || true &&
|
||||
test $(sed -n "/^\(#\|\$\)/p" actual | wc -l) -gt 0
|
||||
'
|
||||
|
||||
get_tag_header reuse $commit commit $time >expect
|
||||
echo "An annotation to be reused" >> expect
|
||||
test_expect_success \
|
||||
'overwriting an annoted tag should use its previous body' '
|
||||
git tag -a -m "An annotation to be reused" reuse &&
|
||||
GIT_EDITOR=true git tag -f -a reuse &&
|
||||
get_tag_msg reuse >actual &&
|
||||
git diff expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -15,7 +15,6 @@ do
|
||||
done
|
||||
unset vi
|
||||
mv e-vi.sh vi
|
||||
PATH=".:$PATH"
|
||||
unset EDITOR VISUAL GIT_EDITOR
|
||||
|
||||
test_expect_success setup '
|
||||
@@ -61,7 +60,7 @@ do
|
||||
;;
|
||||
esac
|
||||
test_expect_success "Using $i" '
|
||||
git commit --amend &&
|
||||
git --exec-path=. commit --amend &&
|
||||
git show -s --pretty=oneline |
|
||||
sed -e "s/^[0-9a-f]* //" >actual &&
|
||||
diff actual expect
|
||||
@@ -83,7 +82,7 @@ do
|
||||
;;
|
||||
esac
|
||||
test_expect_success "Using $i (override)" '
|
||||
git commit --amend &&
|
||||
git --exec-path=. commit --amend &&
|
||||
git show -s --pretty=oneline |
|
||||
sed -e "s/^[0-9a-f]* //" >actual &&
|
||||
diff actual expect
|
||||
|
||||
@@ -59,6 +59,15 @@ test_expect_success 'giving a non existing revision should fail' '
|
||||
check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
|
||||
'
|
||||
|
||||
test_expect_success 'reset --soft with unmerged index should fail' '
|
||||
touch .git/MERGE_HEAD &&
|
||||
echo "100644 44c5b5884550c17758737edcced463447b91d42b 1 un" |
|
||||
git update-index --index-info &&
|
||||
! git reset --soft HEAD &&
|
||||
rm .git/MERGE_HEAD &&
|
||||
git rm --cached -- un
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'giving paths with options different than --mixed should fail' '
|
||||
! git reset --soft -- first &&
|
||||
@@ -409,4 +418,14 @@ test_expect_success 'resetting an unmodified path is a no-op' '
|
||||
git diff-index --cached --exit-code HEAD
|
||||
'
|
||||
|
||||
cat > expect << EOF
|
||||
file2: needs update
|
||||
EOF
|
||||
|
||||
test_expect_success '--mixed refreshes the index' '
|
||||
echo 123 >> file2 &&
|
||||
git reset --mixed HEAD > output &&
|
||||
git diff --exit-code expect output
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -291,4 +291,15 @@ test_expect_success 'clean.requireForce and -f' '
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'core.excludesfile' '
|
||||
|
||||
echo excludes >excludes &&
|
||||
echo included >included &&
|
||||
git config core.excludesfile excludes &&
|
||||
output=$(git clean -n excludes included 2>&1) &&
|
||||
expr "$output" : ".*included" >/dev/null &&
|
||||
! expr "$output" : ".*excludes" >/dev/null
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -93,4 +93,36 @@ test_expect_success 'commit message from file should override template' '
|
||||
commit_msg_is "standard input msg"
|
||||
'
|
||||
|
||||
test_expect_success 'using alternate GIT_INDEX_FILE (1)' '
|
||||
|
||||
cp .git/index saved-index &&
|
||||
(
|
||||
echo some new content >file &&
|
||||
GIT_INDEX_FILE=.git/another_index &&
|
||||
export GIT_INDEX_FILE &&
|
||||
git add file &&
|
||||
git commit -m "commit using another index" &&
|
||||
git diff-index --exit-code HEAD &&
|
||||
git diff-files --exit-code
|
||||
) &&
|
||||
cmp .git/index saved-index >/dev/null
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'using alternate GIT_INDEX_FILE (2)' '
|
||||
|
||||
cp .git/index saved-index &&
|
||||
(
|
||||
rm -f .git/no-such-index &&
|
||||
GIT_INDEX_FILE=.git/no-such-index &&
|
||||
export GIT_INDEX_FILE &&
|
||||
git commit -m "commit using nonexistent index" &&
|
||||
test -z "$(git ls-files)" &&
|
||||
test -z "$(git ls-tree HEAD)"
|
||||
|
||||
) &&
|
||||
cmp .git/index saved-index >/dev/null
|
||||
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
@@ -33,6 +33,16 @@ test_expect_failure \
|
||||
"invalid options 2" \
|
||||
"git-commit -C HEAD -m illegal"
|
||||
|
||||
test_expect_failure \
|
||||
"using paths with -a" \
|
||||
"echo King of the bongo >file &&
|
||||
git-commit -m foo -a file"
|
||||
|
||||
test_expect_failure \
|
||||
"using paths with --interactive" \
|
||||
"echo bong-o-bong >file &&
|
||||
echo 7 | git-commit -m foo --interactive file"
|
||||
|
||||
test_expect_failure \
|
||||
"using invalid commit with -C" \
|
||||
"git-commit -C bogus"
|
||||
|
||||
@@ -53,4 +53,10 @@ test_expect_success 'change file but in unrelated area' "
|
||||
test x\"\`sed -n -e 61p < file\`\" = x6611
|
||||
"
|
||||
|
||||
test_expect_failure 'attempt to dcommit with a dirty index' '
|
||||
echo foo >>file &&
|
||||
git add file &&
|
||||
git svn dcommit
|
||||
'
|
||||
|
||||
test_done
|
||||
|
||||
40
t/t9118-git-svn-funky-branch-names.sh
Executable file
40
t/t9118-git-svn-funky-branch-names.sh
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2007 Eric Wong
|
||||
#
|
||||
|
||||
test_description='git-svn funky branch names'
|
||||
. ./lib-git-svn.sh
|
||||
|
||||
test_expect_success 'setup svnrepo' "
|
||||
mkdir project project/trunk project/branches project/tags &&
|
||||
echo foo > project/trunk/foo &&
|
||||
svn import -m '$test_description' project \"$svnrepo/pr ject\" &&
|
||||
rm -rf project &&
|
||||
svn cp -m 'fun' \"$svnrepo/pr ject/trunk\" \
|
||||
\"$svnrepo/pr ject/branches/fun plugin\" &&
|
||||
svn cp -m 'more fun!' \"$svnrepo/pr ject/branches/fun plugin\" \
|
||||
\"$svnrepo/pr ject/branches/more fun plugin!\" &&
|
||||
start_httpd
|
||||
"
|
||||
|
||||
test_expect_success 'test clone with funky branch names' "
|
||||
git svn clone -s \"$svnrepo/pr ject\" project &&
|
||||
cd project &&
|
||||
git rev-parse 'refs/remotes/fun%20plugin' &&
|
||||
git rev-parse 'refs/remotes/more%20fun%20plugin!' &&
|
||||
cd ..
|
||||
"
|
||||
|
||||
test_expect_success 'test dcommit to funky branch' "
|
||||
cd project &&
|
||||
git reset --hard 'refs/remotes/more%20fun%20plugin!' &&
|
||||
echo hello >> foo &&
|
||||
git commit -m 'hello' -- foo &&
|
||||
git svn dcommit &&
|
||||
cd ..
|
||||
"
|
||||
|
||||
stop_httpd
|
||||
|
||||
test_done
|
||||
@@ -66,9 +66,6 @@ esac
|
||||
tput sgr0 >/dev/null 2>&1 &&
|
||||
color=t
|
||||
|
||||
test "${test_description}" != "" ||
|
||||
error "Test script did not set test_description."
|
||||
|
||||
while test "$#" -ne 0
|
||||
do
|
||||
case "$1" in
|
||||
@@ -77,8 +74,7 @@ do
|
||||
-i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
|
||||
immediate=t; shift ;;
|
||||
-h|--h|--he|--hel|--help)
|
||||
echo "$test_description"
|
||||
exit 0 ;;
|
||||
help=t; shift ;;
|
||||
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
|
||||
verbose=t; shift ;;
|
||||
-q|--q|--qu|--qui|--quie|--quiet)
|
||||
@@ -124,6 +120,15 @@ say () {
|
||||
say_color info "$*"
|
||||
}
|
||||
|
||||
test "${test_description}" != "" ||
|
||||
error "Test script did not set test_description."
|
||||
|
||||
if test "$help" = "t"
|
||||
then
|
||||
echo "$test_description"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exec 5>&1
|
||||
if test "$verbose" = "t"
|
||||
then
|
||||
|
||||
@@ -18,6 +18,7 @@ int main(int argc, const char **argv)
|
||||
OPT_GROUP("string options"),
|
||||
OPT_STRING('s', "string", &string, "string", "get a string"),
|
||||
OPT_STRING(0, "string2", &string, "str", "get another string"),
|
||||
OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"),
|
||||
OPT_END(),
|
||||
};
|
||||
int i;
|
||||
|
||||
4
trace.c
4
trace.c
@@ -72,7 +72,7 @@ void trace_printf(const char *fmt, ...)
|
||||
if (!fd)
|
||||
return;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_init(&buf, 64);
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
|
||||
va_end(ap);
|
||||
@@ -103,7 +103,7 @@ void trace_argv_printf(const char **argv, int count, const char *fmt, ...)
|
||||
if (!fd)
|
||||
return;
|
||||
|
||||
strbuf_init(&buf, 0);
|
||||
strbuf_init(&buf, 64);
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf.buf, strbuf_avail(&buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
@@ -386,7 +386,7 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
|
||||
int argc;
|
||||
int err;
|
||||
|
||||
argv = xmalloc((refspec_nr + 11) * sizeof(char *));
|
||||
argv = xmalloc((refspec_nr + 12) * sizeof(char *));
|
||||
argv[0] = "http-push";
|
||||
argc = 1;
|
||||
if (flags & TRANSPORT_PUSH_ALL)
|
||||
@@ -395,6 +395,8 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
|
||||
argv[argc++] = "--force";
|
||||
if (flags & TRANSPORT_PUSH_DRY_RUN)
|
||||
argv[argc++] = "--dry-run";
|
||||
if (flags & TRANSPORT_PUSH_VERBOSE)
|
||||
argv[argc++] = "--verbose";
|
||||
argv[argc++] = transport->url;
|
||||
while (refspec_nr--)
|
||||
argv[argc++] = *refspec++;
|
||||
@@ -655,7 +657,7 @@ static int git_transport_push(struct transport *transport, int refspec_nr, const
|
||||
int argc;
|
||||
int err;
|
||||
|
||||
argv = xmalloc((refspec_nr + 11) * sizeof(char *));
|
||||
argv = xmalloc((refspec_nr + 12) * sizeof(char *));
|
||||
argv[0] = "send-pack";
|
||||
argc = 1;
|
||||
if (flags & TRANSPORT_PUSH_ALL)
|
||||
@@ -664,6 +666,8 @@ static int git_transport_push(struct transport *transport, int refspec_nr, const
|
||||
argv[argc++] = "--force";
|
||||
if (flags & TRANSPORT_PUSH_DRY_RUN)
|
||||
argv[argc++] = "--dry-run";
|
||||
if (flags & TRANSPORT_PUSH_VERBOSE)
|
||||
argv[argc++] = "--verbose";
|
||||
if (data->receivepack) {
|
||||
char *rp = xmalloc(strlen(data->receivepack) + 16);
|
||||
sprintf(rp, "--receive-pack=%s", data->receivepack);
|
||||
|
||||
@@ -30,6 +30,7 @@ struct transport {
|
||||
#define TRANSPORT_PUSH_ALL 1
|
||||
#define TRANSPORT_PUSH_FORCE 2
|
||||
#define TRANSPORT_PUSH_DRY_RUN 4
|
||||
#define TRANSPORT_PUSH_VERBOSE 8
|
||||
|
||||
/* Returns a transport suitable for the url */
|
||||
struct transport *transport_get(struct remote *, const char *);
|
||||
|
||||
@@ -404,7 +404,7 @@ static void verify_uptodate(struct cache_entry *ce,
|
||||
return;
|
||||
|
||||
if (!lstat(ce->name, &st)) {
|
||||
unsigned changed = ce_match_stat(ce, &st, 1);
|
||||
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
|
||||
if (!changed)
|
||||
return;
|
||||
/*
|
||||
@@ -925,7 +925,7 @@ int oneway_merge(struct cache_entry **src,
|
||||
if (o->reset) {
|
||||
struct stat st;
|
||||
if (lstat(old->name, &st) ||
|
||||
ce_match_stat(old, &st, 1))
|
||||
ce_match_stat(old, &st, CE_MATCH_IGNORE_VALID))
|
||||
old->ce_flags |= htons(CE_UPDATE);
|
||||
}
|
||||
return keep_entry(old, o);
|
||||
|
||||
192
upload-pack.c
192
upload-pack.c
@@ -146,6 +146,7 @@ static void create_pack_file(void)
|
||||
char abort_msg[] = "aborting due to possible repository "
|
||||
"corruption on the remote side.";
|
||||
int buffered = -1;
|
||||
ssize_t sz;
|
||||
const char *argv[10];
|
||||
int arg = 0;
|
||||
|
||||
@@ -179,11 +180,7 @@ static void create_pack_file(void)
|
||||
*/
|
||||
|
||||
while (1) {
|
||||
const char *who;
|
||||
struct pollfd pfd[2];
|
||||
pid_t pid;
|
||||
int status;
|
||||
ssize_t sz;
|
||||
int pe, pu, pollsize;
|
||||
|
||||
reset_timeout();
|
||||
@@ -204,127 +201,94 @@ static void create_pack_file(void)
|
||||
pollsize++;
|
||||
}
|
||||
|
||||
if (pollsize) {
|
||||
if (poll(pfd, pollsize, -1) < 0) {
|
||||
if (errno != EINTR) {
|
||||
error("poll failed, resuming: %s",
|
||||
strerror(errno));
|
||||
sleep(1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
|
||||
/* Data ready; we keep the last byte
|
||||
* to ourselves in case we detect
|
||||
* broken rev-list, so that we can
|
||||
* leave the stream corrupted. This
|
||||
* is unfortunate -- unpack-objects
|
||||
* would happily accept a valid pack
|
||||
* data with trailing garbage, so
|
||||
* appending garbage after we pass all
|
||||
* the pack data is not good enough to
|
||||
* signal breakage to downstream.
|
||||
*/
|
||||
char *cp = data;
|
||||
ssize_t outsz = 0;
|
||||
if (0 <= buffered) {
|
||||
*cp++ = buffered;
|
||||
outsz++;
|
||||
}
|
||||
sz = xread(pack_objects.out, cp,
|
||||
sizeof(data) - outsz);
|
||||
if (0 < sz)
|
||||
;
|
||||
else if (sz == 0) {
|
||||
close(pack_objects.out);
|
||||
pack_objects.out = -1;
|
||||
}
|
||||
else
|
||||
goto fail;
|
||||
sz += outsz;
|
||||
if (1 < sz) {
|
||||
buffered = data[sz-1] & 0xFF;
|
||||
sz--;
|
||||
}
|
||||
else
|
||||
buffered = -1;
|
||||
sz = send_client_data(1, data, sz);
|
||||
if (sz < 0)
|
||||
goto fail;
|
||||
}
|
||||
if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
|
||||
/* Status ready; we ship that in the side-band
|
||||
* or dump to the standard error.
|
||||
*/
|
||||
sz = xread(pack_objects.err, progress,
|
||||
sizeof(progress));
|
||||
if (0 < sz)
|
||||
send_client_data(2, progress, sz);
|
||||
else if (sz == 0) {
|
||||
close(pack_objects.err);
|
||||
pack_objects.err = -1;
|
||||
}
|
||||
else
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (!pollsize)
|
||||
break;
|
||||
|
||||
/* See if the children are still there */
|
||||
if (rev_list.pid || pack_objects.pid) {
|
||||
pid = waitpid(-1, &status, WNOHANG);
|
||||
if (!pid)
|
||||
continue;
|
||||
who = ((pid == rev_list.pid) ? "git-rev-list" :
|
||||
(pid == pack_objects.pid) ? "git-pack-objects" :
|
||||
NULL);
|
||||
if (!who) {
|
||||
if (pid < 0) {
|
||||
error("git-upload-pack: %s",
|
||||
strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
error("git-upload-pack: we weren't "
|
||||
"waiting for %d", pid);
|
||||
continue;
|
||||
if (poll(pfd, pollsize, -1) < 0) {
|
||||
if (errno != EINTR) {
|
||||
error("poll failed, resuming: %s",
|
||||
strerror(errno));
|
||||
sleep(1);
|
||||
}
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) {
|
||||
error("git-upload-pack: %s died with error.",
|
||||
who);
|
||||
goto fail;
|
||||
}
|
||||
if (pid == rev_list.pid)
|
||||
rev_list.pid = 0;
|
||||
if (pid == pack_objects.pid)
|
||||
pack_objects.pid = 0;
|
||||
if (rev_list.pid || pack_objects.pid)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* both died happily */
|
||||
if (pollsize)
|
||||
continue;
|
||||
|
||||
/* flush the data */
|
||||
if (0 <= buffered) {
|
||||
data[0] = buffered;
|
||||
sz = send_client_data(1, data, 1);
|
||||
}
|
||||
if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
|
||||
/* Data ready; we keep the last byte to ourselves
|
||||
* in case we detect broken rev-list, so that we
|
||||
* can leave the stream corrupted. This is
|
||||
* unfortunate -- unpack-objects would happily
|
||||
* accept a valid packdata with trailing garbage,
|
||||
* so appending garbage after we pass all the
|
||||
* pack data is not good enough to signal
|
||||
* breakage to downstream.
|
||||
*/
|
||||
char *cp = data;
|
||||
ssize_t outsz = 0;
|
||||
if (0 <= buffered) {
|
||||
*cp++ = buffered;
|
||||
outsz++;
|
||||
}
|
||||
sz = xread(pack_objects.out, cp,
|
||||
sizeof(data) - outsz);
|
||||
if (0 < sz)
|
||||
;
|
||||
else if (sz == 0) {
|
||||
close(pack_objects.out);
|
||||
pack_objects.out = -1;
|
||||
}
|
||||
else
|
||||
goto fail;
|
||||
sz += outsz;
|
||||
if (1 < sz) {
|
||||
buffered = data[sz-1] & 0xFF;
|
||||
sz--;
|
||||
}
|
||||
else
|
||||
buffered = -1;
|
||||
sz = send_client_data(1, data, sz);
|
||||
if (sz < 0)
|
||||
goto fail;
|
||||
fprintf(stderr, "flushed.\n");
|
||||
}
|
||||
if (use_sideband)
|
||||
packet_flush(1);
|
||||
return;
|
||||
if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
|
||||
/* Status ready; we ship that in the side-band
|
||||
* or dump to the standard error.
|
||||
*/
|
||||
sz = xread(pack_objects.err, progress,
|
||||
sizeof(progress));
|
||||
if (0 < sz)
|
||||
send_client_data(2, progress, sz);
|
||||
else if (sz == 0) {
|
||||
close(pack_objects.err);
|
||||
pack_objects.err = -1;
|
||||
}
|
||||
else
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (finish_command(&pack_objects)) {
|
||||
error("git-upload-pack: git-pack-objects died with error.");
|
||||
goto fail;
|
||||
}
|
||||
if (finish_async(&rev_list))
|
||||
goto fail; /* error was already reported */
|
||||
|
||||
/* flush the data */
|
||||
if (0 <= buffered) {
|
||||
data[0] = buffered;
|
||||
sz = send_client_data(1, data, 1);
|
||||
if (sz < 0)
|
||||
goto fail;
|
||||
fprintf(stderr, "flushed.\n");
|
||||
}
|
||||
if (use_sideband)
|
||||
packet_flush(1);
|
||||
return;
|
||||
|
||||
fail:
|
||||
if (pack_objects.pid)
|
||||
kill(pack_objects.pid, SIGKILL);
|
||||
if (rev_list.pid)
|
||||
kill(rev_list.pid, SIGKILL);
|
||||
send_client_data(3, abort_msg, sizeof(abort_msg));
|
||||
die("git-upload-pack: %s", abort_msg);
|
||||
#else
|
||||
ssize_t sz;
|
||||
char *cp;
|
||||
|
||||
/* We read from pack_objects.out to capture the pack data. */
|
||||
|
||||
15
wt-status.c
15
wt-status.c
@@ -22,7 +22,6 @@ static const char use_add_rm_msg[] =
|
||||
"use \"git add/rm <file>...\" to update what will be committed";
|
||||
static const char use_add_to_include_msg[] =
|
||||
"use \"git add <file>...\" to include in what will be committed";
|
||||
static const char *excludes_file;
|
||||
|
||||
static int parse_status_slot(const char *var, int offset)
|
||||
{
|
||||
@@ -251,22 +250,16 @@ static void wt_status_print_changed(struct wt_status *s)
|
||||
static void wt_status_print_untracked(struct wt_status *s)
|
||||
{
|
||||
struct dir_struct dir;
|
||||
const char *x;
|
||||
int i;
|
||||
int shown_header = 0;
|
||||
|
||||
memset(&dir, 0, sizeof(dir));
|
||||
|
||||
dir.exclude_per_dir = ".gitignore";
|
||||
if (!s->untracked) {
|
||||
dir.show_other_directories = 1;
|
||||
dir.hide_empty_directories = 1;
|
||||
}
|
||||
x = git_path("info/exclude");
|
||||
if (file_exists(x))
|
||||
add_excludes_from_file(&dir, x);
|
||||
if (excludes_file && file_exists(excludes_file))
|
||||
add_excludes_from_file(&dir, excludes_file);
|
||||
setup_standard_excludes(&dir);
|
||||
|
||||
read_directory(&dir, ".", "", 0, NULL);
|
||||
for(i = 0; i < dir.nr; i++) {
|
||||
@@ -364,11 +357,5 @@ int git_status_config(const char *k, const char *v)
|
||||
int slot = parse_status_slot(k, 13);
|
||||
color_parse(v, k, wt_status_colors[slot]);
|
||||
}
|
||||
if (!strcmp(k, "core.excludesfile")) {
|
||||
if (!v)
|
||||
die("core.excludesfile without value");
|
||||
excludes_file = xstrdup(v);
|
||||
return 0;
|
||||
}
|
||||
return git_default_config(k, v);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user