зеркало из https://github.com/microsoft/git.git
Merge branch 'ab/drop-contrib-examples'
* ab/drop-contrib-examples: Remove contrib/examples/*
This commit is contained in:
Коммит
cd94dd02a6
|
@ -1,3 +1,20 @@
|
|||
These are original scripted implementations, kept primarily for their
|
||||
reference value to any aspiring plumbing users who want to learn how
|
||||
pieces can be fit together.
|
||||
This directory used to contain scripted implementations of builtins
|
||||
that have since been rewritten in C.
|
||||
|
||||
They have now been removed, but can be retrieved from an older commit
|
||||
that removed them from this directory.
|
||||
|
||||
They're interesting for their reference value to any aspiring plumbing
|
||||
users who want to learn how pieces can be fit together, but in many
|
||||
cases have drifted enough from the actual implementations Git uses to
|
||||
be instructive.
|
||||
|
||||
Other things that can be useful:
|
||||
|
||||
* Some commands such as git-gc wrap other commands, and what they're
|
||||
doing behind the scenes can be seen by running them under
|
||||
GIT_TRACE=1
|
||||
|
||||
* Doing `git log` on paths matching '*--helper.c' will show
|
||||
incremental effort in the direction of moving existing shell
|
||||
scripts to C.
|
||||
|
|
|
@ -1,575 +0,0 @@
|
|||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
#include "commit.h"
|
||||
#include "sigchain.h"
|
||||
|
||||
static char *get_stdin(void)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
if (strbuf_read(&buf, 0, 1024) < 0) {
|
||||
die_errno("error reading standard input");
|
||||
}
|
||||
return strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
static void show_new(enum object_type type, unsigned char *sha1_new)
|
||||
{
|
||||
fprintf(stderr, " %s: %s\n", type_name(type),
|
||||
find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
|
||||
}
|
||||
|
||||
static int update_ref_env(const char *action,
|
||||
const char *refname,
|
||||
unsigned char *sha1,
|
||||
unsigned char *oldval)
|
||||
{
|
||||
char msg[1024];
|
||||
const char *rla = getenv("GIT_REFLOG_ACTION");
|
||||
|
||||
if (!rla)
|
||||
rla = "(reflog update)";
|
||||
if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg))
|
||||
warning("reflog message too long: %.*s...", 50, msg);
|
||||
return update_ref(msg, refname, sha1, oldval, 0,
|
||||
UPDATE_REFS_QUIET_ON_ERR);
|
||||
}
|
||||
|
||||
static int update_local_ref(const char *name,
|
||||
const char *new_head,
|
||||
const char *note,
|
||||
int verbose, int force)
|
||||
{
|
||||
unsigned char sha1_old[20], sha1_new[20];
|
||||
char oldh[41], newh[41];
|
||||
struct commit *current, *updated;
|
||||
enum object_type type;
|
||||
|
||||
if (get_sha1_hex(new_head, sha1_new))
|
||||
die("malformed object name %s", new_head);
|
||||
|
||||
type = sha1_object_info(sha1_new, NULL);
|
||||
if (type < 0)
|
||||
die("object %s not found", new_head);
|
||||
|
||||
if (!*name) {
|
||||
/* Not storing */
|
||||
if (verbose) {
|
||||
fprintf(stderr, "* fetched %s\n", note);
|
||||
show_new(type, sha1_new);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (get_sha1(name, sha1_old)) {
|
||||
const char *msg;
|
||||
just_store:
|
||||
/* new ref */
|
||||
if (!strncmp(name, "refs/tags/", 10))
|
||||
msg = "storing tag";
|
||||
else
|
||||
msg = "storing head";
|
||||
fprintf(stderr, "* %s: storing %s\n",
|
||||
name, note);
|
||||
show_new(type, sha1_new);
|
||||
return update_ref_env(msg, name, sha1_new, NULL);
|
||||
}
|
||||
|
||||
if (!hashcmp(sha1_old, sha1_new)) {
|
||||
if (verbose) {
|
||||
fprintf(stderr, "* %s: same as %s\n", name, note);
|
||||
show_new(type, sha1_new);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strncmp(name, "refs/tags/", 10)) {
|
||||
fprintf(stderr, "* %s: updating with %s\n", name, note);
|
||||
show_new(type, sha1_new);
|
||||
return update_ref_env("updating tag", name, sha1_new, NULL);
|
||||
}
|
||||
|
||||
current = lookup_commit_reference(sha1_old);
|
||||
updated = lookup_commit_reference(sha1_new);
|
||||
if (!current || !updated)
|
||||
goto just_store;
|
||||
|
||||
strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
|
||||
strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
|
||||
|
||||
if (in_merge_bases(current, updated)) {
|
||||
fprintf(stderr, "* %s: fast-forward to %s\n",
|
||||
name, note);
|
||||
fprintf(stderr, " old..new: %s..%s\n", oldh, newh);
|
||||
return update_ref_env("fast-forward", name, sha1_new, sha1_old);
|
||||
}
|
||||
if (!force) {
|
||||
fprintf(stderr,
|
||||
"* %s: not updating to non-fast-forward %s\n",
|
||||
name, note);
|
||||
fprintf(stderr,
|
||||
" old...new: %s...%s\n", oldh, newh);
|
||||
return 1;
|
||||
}
|
||||
fprintf(stderr,
|
||||
"* %s: forcing update to non-fast-forward %s\n",
|
||||
name, note);
|
||||
fprintf(stderr, " old...new: %s...%s\n", oldh, newh);
|
||||
return update_ref_env("forced-update", name, sha1_new, sha1_old);
|
||||
}
|
||||
|
||||
static int append_fetch_head(FILE *fp,
|
||||
const char *head, const char *remote,
|
||||
const char *remote_name, const char *remote_nick,
|
||||
const char *local_name, int not_for_merge,
|
||||
int verbose, int force)
|
||||
{
|
||||
struct commit *commit;
|
||||
int remote_len, i, note_len;
|
||||
unsigned char sha1[20];
|
||||
char note[1024];
|
||||
const char *what, *kind;
|
||||
|
||||
if (get_sha1(head, sha1))
|
||||
return error("Not a valid object name: %s", head);
|
||||
commit = lookup_commit_reference_gently(sha1, 1);
|
||||
if (!commit)
|
||||
not_for_merge = 1;
|
||||
|
||||
if (!strcmp(remote_name, "HEAD")) {
|
||||
kind = "";
|
||||
what = "";
|
||||
}
|
||||
else if (!strncmp(remote_name, "refs/heads/", 11)) {
|
||||
kind = "branch";
|
||||
what = remote_name + 11;
|
||||
}
|
||||
else if (!strncmp(remote_name, "refs/tags/", 10)) {
|
||||
kind = "tag";
|
||||
what = remote_name + 10;
|
||||
}
|
||||
else if (!strncmp(remote_name, "refs/remotes/", 13)) {
|
||||
kind = "remote-tracking branch";
|
||||
what = remote_name + 13;
|
||||
}
|
||||
else {
|
||||
kind = "";
|
||||
what = remote_name;
|
||||
}
|
||||
|
||||
remote_len = strlen(remote);
|
||||
for (i = remote_len - 1; remote[i] == '/' && 0 <= i; i--)
|
||||
;
|
||||
remote_len = i + 1;
|
||||
if (4 < i && !strncmp(".git", remote + i - 3, 4))
|
||||
remote_len = i - 3;
|
||||
|
||||
note_len = 0;
|
||||
if (*what) {
|
||||
if (*kind)
|
||||
note_len += sprintf(note + note_len, "%s ", kind);
|
||||
note_len += sprintf(note + note_len, "'%s' of ", what);
|
||||
}
|
||||
note_len += sprintf(note + note_len, "%.*s", remote_len, remote);
|
||||
fprintf(fp, "%s\t%s\t%s\n",
|
||||
sha1_to_hex(commit ? commit->object.sha1 : sha1),
|
||||
not_for_merge ? "not-for-merge" : "",
|
||||
note);
|
||||
return update_local_ref(local_name, head, note, verbose, force);
|
||||
}
|
||||
|
||||
static char *keep;
|
||||
static void remove_keep(void)
|
||||
{
|
||||
if (keep && *keep)
|
||||
unlink(keep);
|
||||
}
|
||||
|
||||
static void remove_keep_on_signal(int signo)
|
||||
{
|
||||
remove_keep();
|
||||
sigchain_pop(signo);
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
static char *find_local_name(const char *remote_name, const char *refs,
|
||||
int *force_p, int *not_for_merge_p)
|
||||
{
|
||||
const char *ref = refs;
|
||||
int len = strlen(remote_name);
|
||||
|
||||
while (ref) {
|
||||
const char *next;
|
||||
int single_force, not_for_merge;
|
||||
|
||||
while (*ref == '\n')
|
||||
ref++;
|
||||
if (!*ref)
|
||||
break;
|
||||
next = strchr(ref, '\n');
|
||||
|
||||
single_force = not_for_merge = 0;
|
||||
if (*ref == '+') {
|
||||
single_force = 1;
|
||||
ref++;
|
||||
}
|
||||
if (*ref == '.') {
|
||||
not_for_merge = 1;
|
||||
ref++;
|
||||
if (*ref == '+') {
|
||||
single_force = 1;
|
||||
ref++;
|
||||
}
|
||||
}
|
||||
if (!strncmp(remote_name, ref, len) && ref[len] == ':') {
|
||||
const char *local_part = ref + len + 1;
|
||||
int retlen;
|
||||
|
||||
if (!next)
|
||||
retlen = strlen(local_part);
|
||||
else
|
||||
retlen = next - local_part;
|
||||
*force_p = single_force;
|
||||
*not_for_merge_p = not_for_merge;
|
||||
return xmemdupz(local_part, retlen);
|
||||
}
|
||||
ref = next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int fetch_native_store(FILE *fp,
|
||||
const char *remote,
|
||||
const char *remote_nick,
|
||||
const char *refs,
|
||||
int verbose, int force)
|
||||
{
|
||||
char buffer[1024];
|
||||
int err = 0;
|
||||
|
||||
sigchain_push_common(remove_keep_on_signal);
|
||||
atexit(remove_keep);
|
||||
|
||||
while (fgets(buffer, sizeof(buffer), stdin)) {
|
||||
int len;
|
||||
char *cp;
|
||||
char *local_name;
|
||||
int single_force, not_for_merge;
|
||||
|
||||
for (cp = buffer; *cp && !isspace(*cp); cp++)
|
||||
;
|
||||
if (*cp)
|
||||
*cp++ = 0;
|
||||
len = strlen(cp);
|
||||
if (len && cp[len-1] == '\n')
|
||||
cp[--len] = 0;
|
||||
if (!strcmp(buffer, "failed"))
|
||||
die("Fetch failure: %s", remote);
|
||||
if (!strcmp(buffer, "pack"))
|
||||
continue;
|
||||
if (!strcmp(buffer, "keep")) {
|
||||
char *od = get_object_directory();
|
||||
int len = strlen(od) + strlen(cp) + 50;
|
||||
keep = xmalloc(len);
|
||||
sprintf(keep, "%s/pack/pack-%s.keep", od, cp);
|
||||
continue;
|
||||
}
|
||||
|
||||
local_name = find_local_name(cp, refs,
|
||||
&single_force, ¬_for_merge);
|
||||
if (!local_name)
|
||||
continue;
|
||||
err |= append_fetch_head(fp,
|
||||
buffer, remote, cp, remote_nick,
|
||||
local_name, not_for_merge,
|
||||
verbose, force || single_force);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int parse_reflist(const char *reflist)
|
||||
{
|
||||
const char *ref;
|
||||
|
||||
printf("refs='");
|
||||
for (ref = reflist; ref; ) {
|
||||
const char *next;
|
||||
while (*ref && isspace(*ref))
|
||||
ref++;
|
||||
if (!*ref)
|
||||
break;
|
||||
for (next = ref; *next && !isspace(*next); next++)
|
||||
;
|
||||
printf("\n%.*s", (int)(next - ref), ref);
|
||||
ref = next;
|
||||
}
|
||||
printf("'\n");
|
||||
|
||||
printf("rref='");
|
||||
for (ref = reflist; ref; ) {
|
||||
const char *next, *colon;
|
||||
while (*ref && isspace(*ref))
|
||||
ref++;
|
||||
if (!*ref)
|
||||
break;
|
||||
for (next = ref; *next && !isspace(*next); next++)
|
||||
;
|
||||
if (*ref == '.')
|
||||
ref++;
|
||||
if (*ref == '+')
|
||||
ref++;
|
||||
colon = strchr(ref, ':');
|
||||
putchar('\n');
|
||||
printf("%.*s", (int)((colon ? colon : next) - ref), ref);
|
||||
ref = next;
|
||||
}
|
||||
printf("'\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int expand_refs_wildcard(const char *ls_remote_result, int numrefs,
|
||||
const char **refs)
|
||||
{
|
||||
int i, matchlen, replacelen;
|
||||
int found_one = 0;
|
||||
const char *remote = *refs++;
|
||||
numrefs--;
|
||||
|
||||
if (numrefs == 0) {
|
||||
fprintf(stderr, "Nothing specified for fetching with remote.%s.fetch\n",
|
||||
remote);
|
||||
printf("empty\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < numrefs; i++) {
|
||||
const char *ref = refs[i];
|
||||
const char *lref = ref;
|
||||
const char *colon;
|
||||
const char *tail;
|
||||
const char *ls;
|
||||
const char *next;
|
||||
|
||||
if (*lref == '+')
|
||||
lref++;
|
||||
colon = strchr(lref, ':');
|
||||
tail = lref + strlen(lref);
|
||||
if (!(colon &&
|
||||
2 < colon - lref &&
|
||||
colon[-1] == '*' &&
|
||||
colon[-2] == '/' &&
|
||||
2 < tail - (colon + 1) &&
|
||||
tail[-1] == '*' &&
|
||||
tail[-2] == '/')) {
|
||||
/* not a glob */
|
||||
if (!found_one++)
|
||||
printf("explicit\n");
|
||||
printf("%s\n", ref);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* glob */
|
||||
if (!found_one++)
|
||||
printf("glob\n");
|
||||
|
||||
/* lref to colon-2 is remote hierarchy name;
|
||||
* colon+1 to tail-2 is local.
|
||||
*/
|
||||
matchlen = (colon-1) - lref;
|
||||
replacelen = (tail-1) - (colon+1);
|
||||
for (ls = ls_remote_result; ls; ls = next) {
|
||||
const char *eol;
|
||||
unsigned char sha1[20];
|
||||
int namelen;
|
||||
|
||||
while (*ls && isspace(*ls))
|
||||
ls++;
|
||||
next = strchr(ls, '\n');
|
||||
eol = !next ? (ls + strlen(ls)) : next;
|
||||
if (!memcmp("^{}", eol-3, 3))
|
||||
continue;
|
||||
if (eol - ls < 40)
|
||||
continue;
|
||||
if (get_sha1_hex(ls, sha1))
|
||||
continue;
|
||||
ls += 40;
|
||||
while (ls < eol && isspace(*ls))
|
||||
ls++;
|
||||
/* ls to next (or eol) is the name.
|
||||
* is it identical to lref to colon-2?
|
||||
*/
|
||||
if ((eol - ls) <= matchlen ||
|
||||
strncmp(ls, lref, matchlen))
|
||||
continue;
|
||||
|
||||
/* Yes, it is a match */
|
||||
namelen = eol - ls;
|
||||
if (lref != ref)
|
||||
putchar('+');
|
||||
printf("%.*s:%.*s%.*s\n",
|
||||
namelen, ls,
|
||||
replacelen, colon + 1,
|
||||
namelen - matchlen, ls + matchlen);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_result)
|
||||
{
|
||||
int err = 0;
|
||||
int lrr_count = lrr_count, i, pass;
|
||||
const char *cp;
|
||||
struct lrr {
|
||||
const char *line;
|
||||
const char *name;
|
||||
int namelen;
|
||||
int shown;
|
||||
} *lrr_list = lrr_list;
|
||||
|
||||
for (pass = 0; pass < 2; pass++) {
|
||||
/* pass 0 counts and allocates, pass 1 fills... */
|
||||
cp = ls_remote_result;
|
||||
i = 0;
|
||||
while (1) {
|
||||
const char *np;
|
||||
while (*cp && isspace(*cp))
|
||||
cp++;
|
||||
if (!*cp)
|
||||
break;
|
||||
np = strchrnul(cp, '\n');
|
||||
if (pass) {
|
||||
lrr_list[i].line = cp;
|
||||
lrr_list[i].name = cp + 41;
|
||||
lrr_list[i].namelen = np - (cp + 41);
|
||||
}
|
||||
i++;
|
||||
cp = np;
|
||||
}
|
||||
if (!pass) {
|
||||
lrr_count = i;
|
||||
lrr_list = xcalloc(lrr_count, sizeof(*lrr_list));
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
const char *next;
|
||||
int rreflen;
|
||||
int i;
|
||||
|
||||
while (*rref && isspace(*rref))
|
||||
rref++;
|
||||
if (!*rref)
|
||||
break;
|
||||
next = strchrnul(rref, '\n');
|
||||
rreflen = next - rref;
|
||||
|
||||
for (i = 0; i < lrr_count; i++) {
|
||||
struct lrr *lrr = &(lrr_list[i]);
|
||||
|
||||
if (rreflen == lrr->namelen &&
|
||||
!memcmp(lrr->name, rref, rreflen)) {
|
||||
if (!lrr->shown)
|
||||
printf("%.*s\n",
|
||||
sha1_only ? 40 : lrr->namelen + 41,
|
||||
lrr->line);
|
||||
lrr->shown = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lrr_count <= i) {
|
||||
error("pick-rref: %.*s not found", rreflen, rref);
|
||||
err = 1;
|
||||
}
|
||||
rref = next;
|
||||
}
|
||||
free(lrr_list);
|
||||
return err;
|
||||
}
|
||||
|
||||
int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int verbose = 0;
|
||||
int force = 0;
|
||||
int sopt = 0;
|
||||
|
||||
while (1 < argc) {
|
||||
const char *arg = argv[1];
|
||||
if (!strcmp("-v", arg))
|
||||
verbose = 1;
|
||||
else if (!strcmp("-f", arg))
|
||||
force = 1;
|
||||
else if (!strcmp("-s", arg))
|
||||
sopt = 1;
|
||||
else
|
||||
break;
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
if (argc <= 1)
|
||||
return error("Missing subcommand");
|
||||
|
||||
if (!strcmp("append-fetch-head", argv[1])) {
|
||||
int result;
|
||||
FILE *fp;
|
||||
char *filename;
|
||||
|
||||
if (argc != 8)
|
||||
return error("append-fetch-head takes 6 args");
|
||||
filename = git_path_fetch_head();
|
||||
fp = fopen(filename, "a");
|
||||
if (!fp)
|
||||
return error("cannot open %s: %s", filename, strerror(errno));
|
||||
result = append_fetch_head(fp, argv[2], argv[3],
|
||||
argv[4], argv[5],
|
||||
argv[6], !!argv[7][0],
|
||||
verbose, force);
|
||||
fclose(fp);
|
||||
return result;
|
||||
}
|
||||
if (!strcmp("native-store", argv[1])) {
|
||||
int result;
|
||||
FILE *fp;
|
||||
char *filename;
|
||||
|
||||
if (argc != 5)
|
||||
return error("fetch-native-store takes 3 args");
|
||||
filename = git_path_fetch_head();
|
||||
fp = fopen(filename, "a");
|
||||
if (!fp)
|
||||
return error("cannot open %s: %s", filename, strerror(errno));
|
||||
result = fetch_native_store(fp, argv[2], argv[3], argv[4],
|
||||
verbose, force);
|
||||
fclose(fp);
|
||||
return result;
|
||||
}
|
||||
if (!strcmp("parse-reflist", argv[1])) {
|
||||
const char *reflist;
|
||||
if (argc != 3)
|
||||
return error("parse-reflist takes 1 arg");
|
||||
reflist = argv[2];
|
||||
if (!strcmp(reflist, "-"))
|
||||
reflist = get_stdin();
|
||||
return parse_reflist(reflist);
|
||||
}
|
||||
if (!strcmp("pick-rref", argv[1])) {
|
||||
const char *ls_remote_result;
|
||||
if (argc != 4)
|
||||
return error("pick-rref takes 2 args");
|
||||
ls_remote_result = argv[3];
|
||||
if (!strcmp(ls_remote_result, "-"))
|
||||
ls_remote_result = get_stdin();
|
||||
return pick_rref(sopt, argv[2], ls_remote_result);
|
||||
}
|
||||
if (!strcmp("expand-refs-wildcard", argv[1])) {
|
||||
const char *reflist;
|
||||
if (argc < 4)
|
||||
return error("expand-refs-wildcard takes at least 2 args");
|
||||
reflist = argv[2];
|
||||
if (!strcmp(reflist, "-"))
|
||||
reflist = get_stdin();
|
||||
return expand_refs_wildcard(reflist, argc - 3, argv + 3);
|
||||
}
|
||||
|
||||
return error("Unknown subcommand: %s", argv[1]);
|
||||
}
|
|
@ -1,975 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005, 2006 Junio C Hamano
|
||||
|
||||
SUBDIRECTORY_OK=Yes
|
||||
OPTIONS_KEEPDASHDASH=
|
||||
OPTIONS_STUCKLONG=t
|
||||
OPTIONS_SPEC="\
|
||||
git am [options] [(<mbox>|<Maildir>)...]
|
||||
git am [options] (--continue | --skip | --abort)
|
||||
--
|
||||
i,interactive run interactively
|
||||
b,binary* (historical option -- no-op)
|
||||
3,3way allow fall back on 3way merging if needed
|
||||
q,quiet be quiet
|
||||
s,signoff add a Signed-off-by line to the commit message
|
||||
u,utf8 recode into utf8 (default)
|
||||
k,keep pass -k flag to git-mailinfo
|
||||
keep-non-patch pass -b flag to git-mailinfo
|
||||
m,message-id pass -m flag to git-mailinfo
|
||||
keep-cr pass --keep-cr flag to git-mailsplit for mbox format
|
||||
no-keep-cr do not pass --keep-cr flag to git-mailsplit independent of am.keepcr
|
||||
c,scissors strip everything before a scissors line
|
||||
whitespace= pass it through git-apply
|
||||
ignore-space-change pass it through git-apply
|
||||
ignore-whitespace pass it through git-apply
|
||||
directory= pass it through git-apply
|
||||
exclude= pass it through git-apply
|
||||
include= pass it through git-apply
|
||||
C= pass it through git-apply
|
||||
p= pass it through git-apply
|
||||
patch-format= format the patch(es) are in
|
||||
reject pass it through git-apply
|
||||
resolvemsg= override error message when patch failure occurs
|
||||
continue continue applying patches after resolving a conflict
|
||||
r,resolved synonyms for --continue
|
||||
skip skip the current patch
|
||||
abort restore the original branch and abort the patching operation.
|
||||
committer-date-is-author-date lie about committer date
|
||||
ignore-date use current timestamp for author date
|
||||
rerere-autoupdate update the index with reused conflict resolution if possible
|
||||
S,gpg-sign? GPG-sign commits
|
||||
rebasing* (internal use for git-rebase)"
|
||||
|
||||
. git-sh-setup
|
||||
. git-sh-i18n
|
||||
prefix=$(git rev-parse --show-prefix)
|
||||
set_reflog_action am
|
||||
require_work_tree
|
||||
cd_to_toplevel
|
||||
|
||||
git var GIT_COMMITTER_IDENT >/dev/null ||
|
||||
die "$(gettext "You need to set your committer info first")"
|
||||
|
||||
if git rev-parse --verify -q HEAD >/dev/null
|
||||
then
|
||||
HAS_HEAD=yes
|
||||
else
|
||||
HAS_HEAD=
|
||||
fi
|
||||
|
||||
cmdline="git am"
|
||||
if test '' != "$interactive"
|
||||
then
|
||||
cmdline="$cmdline -i"
|
||||
fi
|
||||
if test '' != "$threeway"
|
||||
then
|
||||
cmdline="$cmdline -3"
|
||||
fi
|
||||
|
||||
empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
|
||||
|
||||
sq () {
|
||||
git rev-parse --sq-quote "$@"
|
||||
}
|
||||
|
||||
stop_here () {
|
||||
echo "$1" >"$dotest/next"
|
||||
git rev-parse --verify -q HEAD >"$dotest/abort-safety"
|
||||
exit 1
|
||||
}
|
||||
|
||||
safe_to_abort () {
|
||||
if test -f "$dotest/dirtyindex"
|
||||
then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! test -f "$dotest/abort-safety"
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
|
||||
abort_safety=$(cat "$dotest/abort-safety")
|
||||
if test "z$(git rev-parse --verify -q HEAD)" = "z$abort_safety"
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
gettextln "You seem to have moved HEAD since the last 'am' failure.
|
||||
Not rewinding to ORIG_HEAD" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
stop_here_user_resolve () {
|
||||
if [ -n "$resolvemsg" ]; then
|
||||
printf '%s\n' "$resolvemsg"
|
||||
stop_here $1
|
||||
fi
|
||||
eval_gettextln "When you have resolved this problem, run \"\$cmdline --continue\".
|
||||
If you prefer to skip this patch, run \"\$cmdline --skip\" instead.
|
||||
To restore the original branch and stop patching, run \"\$cmdline --abort\"."
|
||||
|
||||
stop_here $1
|
||||
}
|
||||
|
||||
go_next () {
|
||||
rm -f "$dotest/$msgnum" "$dotest/msg" "$dotest/msg-clean" \
|
||||
"$dotest/patch" "$dotest/info"
|
||||
echo "$next" >"$dotest/next"
|
||||
this=$next
|
||||
}
|
||||
|
||||
cannot_fallback () {
|
||||
echo "$1"
|
||||
gettextln "Cannot fall back to three-way merge."
|
||||
exit 1
|
||||
}
|
||||
|
||||
fall_back_3way () {
|
||||
O_OBJECT=$(cd "$GIT_OBJECT_DIRECTORY" && pwd)
|
||||
|
||||
rm -fr "$dotest"/patch-merge-*
|
||||
mkdir "$dotest/patch-merge-tmp-dir"
|
||||
|
||||
# First see if the patch records the index info that we can use.
|
||||
cmd="git apply $git_apply_opt --build-fake-ancestor" &&
|
||||
cmd="$cmd "'"$dotest/patch-merge-tmp-index" "$dotest/patch"' &&
|
||||
eval "$cmd" &&
|
||||
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
|
||||
git write-tree >"$dotest/patch-merge-base+" ||
|
||||
cannot_fallback "$(gettext "Repository lacks necessary blobs to fall back on 3-way merge.")"
|
||||
|
||||
say "$(gettext "Using index info to reconstruct a base tree...")"
|
||||
|
||||
cmd='GIT_INDEX_FILE="$dotest/patch-merge-tmp-index"'
|
||||
|
||||
if test -z "$GIT_QUIET"
|
||||
then
|
||||
eval "$cmd git diff-index --cached --diff-filter=AM --name-status HEAD"
|
||||
fi
|
||||
|
||||
cmd="$cmd git apply --cached $git_apply_opt"' <"$dotest/patch"'
|
||||
if eval "$cmd"
|
||||
then
|
||||
mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
|
||||
mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
|
||||
else
|
||||
cannot_fallback "$(gettext "Did you hand edit your patch?
|
||||
It does not apply to blobs recorded in its index.")"
|
||||
fi
|
||||
|
||||
test -f "$dotest/patch-merge-index" &&
|
||||
his_tree=$(GIT_INDEX_FILE="$dotest/patch-merge-index" git write-tree) &&
|
||||
orig_tree=$(cat "$dotest/patch-merge-base") &&
|
||||
rm -fr "$dotest"/patch-merge-* || exit 1
|
||||
|
||||
say "$(gettext "Falling back to patching base and 3-way merge...")"
|
||||
|
||||
# This is not so wrong. Depending on which base we picked,
|
||||
# orig_tree may be wildly different from ours, but his_tree
|
||||
# has the same set of wildly different changes in parts the
|
||||
# patch did not touch, so recursive ends up canceling them,
|
||||
# saying that we reverted all those changes.
|
||||
|
||||
eval GITHEAD_$his_tree='"$FIRSTLINE"'
|
||||
export GITHEAD_$his_tree
|
||||
if test -n "$GIT_QUIET"
|
||||
then
|
||||
GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
|
||||
fi
|
||||
our_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree)
|
||||
git-merge-recursive $orig_tree -- $our_tree $his_tree || {
|
||||
git rerere $allow_rerere_autoupdate
|
||||
die "$(gettext "Failed to merge in the changes.")"
|
||||
}
|
||||
unset GITHEAD_$his_tree
|
||||
}
|
||||
|
||||
clean_abort () {
|
||||
test $# = 0 || echo >&2 "$@"
|
||||
rm -fr "$dotest"
|
||||
exit 1
|
||||
}
|
||||
|
||||
patch_format=
|
||||
|
||||
check_patch_format () {
|
||||
# early return if patch_format was set from the command line
|
||||
if test -n "$patch_format"
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# we default to mbox format if input is from stdin and for
|
||||
# directories
|
||||
if test $# = 0 || test "x$1" = "x-" || test -d "$1"
|
||||
then
|
||||
patch_format=mbox
|
||||
return 0
|
||||
fi
|
||||
|
||||
# otherwise, check the first few non-blank lines of the first
|
||||
# patch to try to detect its format
|
||||
{
|
||||
# Start from first line containing non-whitespace
|
||||
l1=
|
||||
while test -z "$l1"
|
||||
do
|
||||
read l1 || break
|
||||
done
|
||||
read l2
|
||||
read l3
|
||||
case "$l1" in
|
||||
"From "* | "From: "*)
|
||||
patch_format=mbox
|
||||
;;
|
||||
'# This series applies on GIT commit'*)
|
||||
patch_format=stgit-series
|
||||
;;
|
||||
"# HG changeset patch")
|
||||
patch_format=hg
|
||||
;;
|
||||
*)
|
||||
# if the second line is empty and the third is
|
||||
# a From, Author or Date entry, this is very
|
||||
# likely an StGIT patch
|
||||
case "$l2,$l3" in
|
||||
,"From: "* | ,"Author: "* | ,"Date: "*)
|
||||
patch_format=stgit
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
if test -z "$patch_format" &&
|
||||
test -n "$l1" &&
|
||||
test -n "$l2" &&
|
||||
test -n "$l3"
|
||||
then
|
||||
# This begins with three non-empty lines. Is this a
|
||||
# piece of e-mail a-la RFC2822? Grab all the headers,
|
||||
# discarding the indented remainder of folded lines,
|
||||
# and see if it looks like that they all begin with the
|
||||
# header field names...
|
||||
tr -d '\015' <"$1" |
|
||||
sed -n -e '/^$/q' -e '/^[ ]/d' -e p |
|
||||
sane_egrep -v '^[!-9;-~]+:' >/dev/null ||
|
||||
patch_format=mbox
|
||||
fi
|
||||
} < "$1" || clean_abort
|
||||
}
|
||||
|
||||
split_patches () {
|
||||
case "$patch_format" in
|
||||
mbox)
|
||||
if test t = "$keepcr"
|
||||
then
|
||||
keep_cr=--keep-cr
|
||||
else
|
||||
keep_cr=
|
||||
fi
|
||||
git mailsplit -d"$prec" -o"$dotest" -b $keep_cr -- "$@" > "$dotest/last" ||
|
||||
clean_abort
|
||||
;;
|
||||
stgit-series)
|
||||
if test $# -ne 1
|
||||
then
|
||||
clean_abort "$(gettext "Only one StGIT patch series can be applied at once")"
|
||||
fi
|
||||
series_dir=$(dirname "$1")
|
||||
series_file="$1"
|
||||
shift
|
||||
{
|
||||
set x
|
||||
while read filename
|
||||
do
|
||||
set "$@" "$series_dir/$filename"
|
||||
done
|
||||
# remove the safety x
|
||||
shift
|
||||
# remove the arg coming from the first-line comment
|
||||
shift
|
||||
} < "$series_file" || clean_abort
|
||||
# set the patch format appropriately
|
||||
patch_format=stgit
|
||||
# now handle the actual StGIT patches
|
||||
split_patches "$@"
|
||||
;;
|
||||
stgit)
|
||||
this=0
|
||||
test 0 -eq "$#" && set -- -
|
||||
for stgit in "$@"
|
||||
do
|
||||
this=$(expr "$this" + 1)
|
||||
msgnum=$(printf "%0${prec}d" $this)
|
||||
# Perl version of StGIT parse_patch. The first nonemptyline
|
||||
# not starting with Author, From or Date is the
|
||||
# subject, and the body starts with the next nonempty
|
||||
# line not starting with Author, From or Date
|
||||
@@PERL@@ -ne 'BEGIN { $subject = 0 }
|
||||
if ($subject > 1) { print ; }
|
||||
elsif (/^\s+$/) { next ; }
|
||||
elsif (/^Author:/) { s/Author/From/ ; print ;}
|
||||
elsif (/^(From|Date)/) { print ; }
|
||||
elsif ($subject) {
|
||||
$subject = 2 ;
|
||||
print "\n" ;
|
||||
print ;
|
||||
} else {
|
||||
print "Subject: ", $_ ;
|
||||
$subject = 1;
|
||||
}
|
||||
' -- "$stgit" >"$dotest/$msgnum" || clean_abort
|
||||
done
|
||||
echo "$this" > "$dotest/last"
|
||||
this=
|
||||
msgnum=
|
||||
;;
|
||||
hg)
|
||||
this=0
|
||||
test 0 -eq "$#" && set -- -
|
||||
for hg in "$@"
|
||||
do
|
||||
this=$(( $this + 1 ))
|
||||
msgnum=$(printf "%0${prec}d" $this)
|
||||
# hg stores changeset metadata in #-commented lines preceding
|
||||
# the commit message and diff(s). The only metadata we care about
|
||||
# are the User and Date (Node ID and Parent are hashes which are
|
||||
# only relevant to the hg repository and thus not useful to us)
|
||||
# Since we cannot guarantee that the commit message is in
|
||||
# git-friendly format, we put no Subject: line and just consume
|
||||
# all of the message as the body
|
||||
LANG=C LC_ALL=C @@PERL@@ -M'POSIX qw(strftime)' -ne 'BEGIN { $subject = 0 }
|
||||
if ($subject) { print ; }
|
||||
elsif (/^\# User /) { s/\# User/From:/ ; print ; }
|
||||
elsif (/^\# Date /) {
|
||||
my ($hashsign, $str, $time, $tz) = split ;
|
||||
$tz_str = sprintf "%+05d", (0-$tz)/36;
|
||||
print "Date: " .
|
||||
strftime("%a, %d %b %Y %H:%M:%S ",
|
||||
gmtime($time-$tz))
|
||||
. "$tz_str\n";
|
||||
} elsif (/^\# /) { next ; }
|
||||
else {
|
||||
print "\n", $_ ;
|
||||
$subject = 1;
|
||||
}
|
||||
' -- "$hg" >"$dotest/$msgnum" || clean_abort
|
||||
done
|
||||
echo "$this" >"$dotest/last"
|
||||
this=
|
||||
msgnum=
|
||||
;;
|
||||
*)
|
||||
if test -n "$patch_format"
|
||||
then
|
||||
clean_abort "$(eval_gettext "Patch format \$patch_format is not supported.")"
|
||||
else
|
||||
clean_abort "$(gettext "Patch format detection failed.")"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
prec=4
|
||||
dotest="$GIT_DIR/rebase-apply"
|
||||
sign= utf8=t keep= keepcr= skip= interactive= resolved= rebasing= abort=
|
||||
messageid= resolvemsg= resume= scissors= no_inbody_headers=
|
||||
git_apply_opt=
|
||||
committer_date_is_author_date=
|
||||
ignore_date=
|
||||
allow_rerere_autoupdate=
|
||||
gpg_sign_opt=
|
||||
threeway=
|
||||
|
||||
if test "$(git config --bool --get am.messageid)" = true
|
||||
then
|
||||
messageid=t
|
||||
fi
|
||||
|
||||
if test "$(git config --bool --get am.keepcr)" = true
|
||||
then
|
||||
keepcr=t
|
||||
fi
|
||||
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-i|--interactive)
|
||||
interactive=t ;;
|
||||
-b|--binary)
|
||||
gettextln >&2 "The -b/--binary option has been a no-op for long time, and
|
||||
it will be removed. Please do not use it anymore."
|
||||
;;
|
||||
-3|--3way)
|
||||
threeway=t ;;
|
||||
-s|--signoff)
|
||||
sign=t ;;
|
||||
-u|--utf8)
|
||||
utf8=t ;; # this is now default
|
||||
--no-utf8)
|
||||
utf8= ;;
|
||||
-m|--message-id)
|
||||
messageid=t ;;
|
||||
--no-message-id)
|
||||
messageid=f ;;
|
||||
-k|--keep)
|
||||
keep=t ;;
|
||||
--keep-non-patch)
|
||||
keep=b ;;
|
||||
-c|--scissors)
|
||||
scissors=t ;;
|
||||
--no-scissors)
|
||||
scissors=f ;;
|
||||
-r|--resolved|--continue)
|
||||
resolved=t ;;
|
||||
--skip)
|
||||
skip=t ;;
|
||||
--abort)
|
||||
abort=t ;;
|
||||
--rebasing)
|
||||
rebasing=t threeway=t ;;
|
||||
--resolvemsg=*)
|
||||
resolvemsg="${1#--resolvemsg=}" ;;
|
||||
--whitespace=*|--directory=*|--exclude=*|--include=*)
|
||||
git_apply_opt="$git_apply_opt $(sq "$1")" ;;
|
||||
-C*|-p*)
|
||||
git_apply_opt="$git_apply_opt $(sq "$1")" ;;
|
||||
--patch-format=*)
|
||||
patch_format="${1#--patch-format=}" ;;
|
||||
--reject|--ignore-whitespace|--ignore-space-change)
|
||||
git_apply_opt="$git_apply_opt $1" ;;
|
||||
--committer-date-is-author-date)
|
||||
committer_date_is_author_date=t ;;
|
||||
--ignore-date)
|
||||
ignore_date=t ;;
|
||||
--rerere-autoupdate|--no-rerere-autoupdate)
|
||||
allow_rerere_autoupdate="$1" ;;
|
||||
-q|--quiet)
|
||||
GIT_QUIET=t ;;
|
||||
--keep-cr)
|
||||
keepcr=t ;;
|
||||
--no-keep-cr)
|
||||
keepcr=f ;;
|
||||
--gpg-sign)
|
||||
gpg_sign_opt=-S ;;
|
||||
--gpg-sign=*)
|
||||
gpg_sign_opt="-S${1#--gpg-sign=}" ;;
|
||||
--)
|
||||
shift; break ;;
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# If the dotest directory exists, but we have finished applying all the
|
||||
# patches in them, clear it out.
|
||||
if test -d "$dotest" &&
|
||||
test -f "$dotest/last" &&
|
||||
test -f "$dotest/next" &&
|
||||
last=$(cat "$dotest/last") &&
|
||||
next=$(cat "$dotest/next") &&
|
||||
test $# != 0 &&
|
||||
test "$next" -gt "$last"
|
||||
then
|
||||
rm -fr "$dotest"
|
||||
fi
|
||||
|
||||
if test -d "$dotest" && test -f "$dotest/last" && test -f "$dotest/next"
|
||||
then
|
||||
case "$#,$skip$resolved$abort" in
|
||||
0,*t*)
|
||||
# Explicit resume command and we do not have file, so
|
||||
# we are happy.
|
||||
: ;;
|
||||
0,)
|
||||
# No file input but without resume parameters; catch
|
||||
# user error to feed us a patch from standard input
|
||||
# when there is already $dotest. This is somewhat
|
||||
# unreliable -- stdin could be /dev/null for example
|
||||
# and the caller did not intend to feed us a patch but
|
||||
# wanted to continue unattended.
|
||||
test -t 0
|
||||
;;
|
||||
*)
|
||||
false
|
||||
;;
|
||||
esac ||
|
||||
die "$(eval_gettext "previous rebase directory \$dotest still exists but mbox given.")"
|
||||
resume=yes
|
||||
|
||||
case "$skip,$abort" in
|
||||
t,t)
|
||||
die "$(gettext "Please make up your mind. --skip or --abort?")"
|
||||
;;
|
||||
t,)
|
||||
git rerere clear
|
||||
head_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree) &&
|
||||
git read-tree --reset -u $head_tree $head_tree &&
|
||||
index_tree=$(git write-tree) &&
|
||||
git read-tree -m -u $index_tree $head_tree
|
||||
git read-tree -m $head_tree
|
||||
;;
|
||||
,t)
|
||||
if test -f "$dotest/rebasing"
|
||||
then
|
||||
exec git rebase --abort
|
||||
fi
|
||||
git rerere clear
|
||||
if safe_to_abort
|
||||
then
|
||||
head_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree) &&
|
||||
git read-tree --reset -u $head_tree $head_tree &&
|
||||
index_tree=$(git write-tree) &&
|
||||
orig_head=$(git rev-parse --verify -q ORIG_HEAD || echo $empty_tree) &&
|
||||
git read-tree -m -u $index_tree $orig_head
|
||||
if git rev-parse --verify -q ORIG_HEAD >/dev/null 2>&1
|
||||
then
|
||||
git reset ORIG_HEAD
|
||||
else
|
||||
git read-tree $empty_tree
|
||||
curr_branch=$(git symbolic-ref HEAD 2>/dev/null) &&
|
||||
git update-ref -d $curr_branch
|
||||
fi
|
||||
fi
|
||||
rm -fr "$dotest"
|
||||
exit ;;
|
||||
esac
|
||||
rm -f "$dotest/dirtyindex"
|
||||
else
|
||||
# Possible stray $dotest directory in the independent-run
|
||||
# case; in the --rebasing case, it is upto the caller
|
||||
# (git-rebase--am) to take care of stray directories.
|
||||
if test -d "$dotest" && test -z "$rebasing"
|
||||
then
|
||||
case "$skip,$resolved,$abort" in
|
||||
,,t)
|
||||
rm -fr "$dotest"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
die "$(eval_gettext "Stray \$dotest directory found.
|
||||
Use \"git am --abort\" to remove it.")"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Make sure we are not given --skip, --continue, or --abort
|
||||
test "$skip$resolved$abort" = "" ||
|
||||
die "$(gettext "Resolve operation not in progress, we are not resuming.")"
|
||||
|
||||
# Start afresh.
|
||||
mkdir -p "$dotest" || exit
|
||||
|
||||
if test -n "$prefix" && test $# != 0
|
||||
then
|
||||
first=t
|
||||
for arg
|
||||
do
|
||||
test -n "$first" && {
|
||||
set x
|
||||
first=
|
||||
}
|
||||
if is_absolute_path "$arg"
|
||||
then
|
||||
set "$@" "$arg"
|
||||
else
|
||||
set "$@" "$prefix$arg"
|
||||
fi
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
check_patch_format "$@"
|
||||
|
||||
split_patches "$@"
|
||||
|
||||
# -i can and must be given when resuming; everything
|
||||
# else is kept
|
||||
echo " $git_apply_opt" >"$dotest/apply-opt"
|
||||
echo "$threeway" >"$dotest/threeway"
|
||||
echo "$sign" >"$dotest/sign"
|
||||
echo "$utf8" >"$dotest/utf8"
|
||||
echo "$keep" >"$dotest/keep"
|
||||
echo "$messageid" >"$dotest/messageid"
|
||||
echo "$scissors" >"$dotest/scissors"
|
||||
echo "$no_inbody_headers" >"$dotest/no_inbody_headers"
|
||||
echo "$GIT_QUIET" >"$dotest/quiet"
|
||||
echo 1 >"$dotest/next"
|
||||
if test -n "$rebasing"
|
||||
then
|
||||
: >"$dotest/rebasing"
|
||||
else
|
||||
: >"$dotest/applying"
|
||||
if test -n "$HAS_HEAD"
|
||||
then
|
||||
git update-ref ORIG_HEAD HEAD
|
||||
else
|
||||
git update-ref -d ORIG_HEAD >/dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
git update-index -q --refresh
|
||||
|
||||
case "$resolved" in
|
||||
'')
|
||||
case "$HAS_HEAD" in
|
||||
'')
|
||||
files=$(git ls-files) ;;
|
||||
?*)
|
||||
files=$(git diff-index --cached --name-only HEAD --) ;;
|
||||
esac || exit
|
||||
if test "$files"
|
||||
then
|
||||
test -n "$HAS_HEAD" && : >"$dotest/dirtyindex"
|
||||
die "$(eval_gettext "Dirty index: cannot apply patches (dirty: \$files)")"
|
||||
fi
|
||||
esac
|
||||
|
||||
# Now, decide what command line options we will give to the git
|
||||
# commands we invoke, based on the result of parsing command line
|
||||
# options and previous invocation state stored in $dotest/ files.
|
||||
|
||||
if test "$(cat "$dotest/utf8")" = t
|
||||
then
|
||||
utf8=-u
|
||||
else
|
||||
utf8=-n
|
||||
fi
|
||||
keep=$(cat "$dotest/keep")
|
||||
case "$keep" in
|
||||
t)
|
||||
keep=-k ;;
|
||||
b)
|
||||
keep=-b ;;
|
||||
*)
|
||||
keep= ;;
|
||||
esac
|
||||
case "$(cat "$dotest/messageid")" in
|
||||
t)
|
||||
messageid=-m ;;
|
||||
f)
|
||||
messageid= ;;
|
||||
esac
|
||||
case "$(cat "$dotest/scissors")" in
|
||||
t)
|
||||
scissors=--scissors ;;
|
||||
f)
|
||||
scissors=--no-scissors ;;
|
||||
esac
|
||||
if test "$(cat "$dotest/no_inbody_headers")" = t
|
||||
then
|
||||
no_inbody_headers=--no-inbody-headers
|
||||
else
|
||||
no_inbody_headers=
|
||||
fi
|
||||
if test "$(cat "$dotest/quiet")" = t
|
||||
then
|
||||
GIT_QUIET=t
|
||||
fi
|
||||
if test "$(cat "$dotest/threeway")" = t
|
||||
then
|
||||
threeway=t
|
||||
fi
|
||||
git_apply_opt=$(cat "$dotest/apply-opt")
|
||||
if test "$(cat "$dotest/sign")" = t
|
||||
then
|
||||
SIGNOFF=$(git var GIT_COMMITTER_IDENT | sed -e '
|
||||
s/>.*/>/
|
||||
s/^/Signed-off-by: /'
|
||||
)
|
||||
else
|
||||
SIGNOFF=
|
||||
fi
|
||||
|
||||
last=$(cat "$dotest/last")
|
||||
this=$(cat "$dotest/next")
|
||||
if test "$skip" = t
|
||||
then
|
||||
this=$(expr "$this" + 1)
|
||||
resume=
|
||||
fi
|
||||
|
||||
while test "$this" -le "$last"
|
||||
do
|
||||
msgnum=$(printf "%0${prec}d" $this)
|
||||
next=$(expr "$this" + 1)
|
||||
test -f "$dotest/$msgnum" || {
|
||||
resume=
|
||||
go_next
|
||||
continue
|
||||
}
|
||||
|
||||
# If we are not resuming, parse and extract the patch information
|
||||
# into separate files:
|
||||
# - info records the authorship and title
|
||||
# - msg is the rest of commit log message
|
||||
# - patch is the patch body.
|
||||
#
|
||||
# When we are resuming, these files are either already prepared
|
||||
# by the user, or the user can tell us to do so by --continue flag.
|
||||
case "$resume" in
|
||||
'')
|
||||
if test -f "$dotest/rebasing"
|
||||
then
|
||||
commit=$(sed -e 's/^From \([0-9a-f]*\) .*/\1/' \
|
||||
-e q "$dotest/$msgnum") &&
|
||||
test "$(git cat-file -t "$commit")" = commit ||
|
||||
stop_here $this
|
||||
git cat-file commit "$commit" |
|
||||
sed -e '1,/^$/d' >"$dotest/msg-clean"
|
||||
echo "$commit" >"$dotest/original-commit"
|
||||
get_author_ident_from_commit "$commit" >"$dotest/author-script"
|
||||
git diff-tree --root --binary --full-index "$commit" >"$dotest/patch"
|
||||
else
|
||||
git mailinfo $keep $no_inbody_headers $messageid $scissors $utf8 "$dotest/msg" "$dotest/patch" \
|
||||
<"$dotest/$msgnum" >"$dotest/info" ||
|
||||
stop_here $this
|
||||
|
||||
# skip pine's internal folder data
|
||||
sane_grep '^Author: Mail System Internal Data$' \
|
||||
<"$dotest"/info >/dev/null &&
|
||||
go_next && continue
|
||||
|
||||
test -s "$dotest/patch" || {
|
||||
eval_gettextln "Patch is empty. Was it split wrong?
|
||||
If you would prefer to skip this patch, instead run \"\$cmdline --skip\".
|
||||
To restore the original branch and stop patching run \"\$cmdline --abort\"."
|
||||
stop_here $this
|
||||
}
|
||||
rm -f "$dotest/original-commit" "$dotest/author-script"
|
||||
{
|
||||
sed -n '/^Subject/ s/Subject: //p' "$dotest/info"
|
||||
echo
|
||||
cat "$dotest/msg"
|
||||
} |
|
||||
git stripspace > "$dotest/msg-clean"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if test -f "$dotest/author-script"
|
||||
then
|
||||
eval $(cat "$dotest/author-script")
|
||||
else
|
||||
GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
|
||||
GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
|
||||
GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
|
||||
fi
|
||||
|
||||
if test -z "$GIT_AUTHOR_EMAIL"
|
||||
then
|
||||
gettextln "Patch does not have a valid e-mail address."
|
||||
stop_here $this
|
||||
fi
|
||||
|
||||
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
|
||||
|
||||
case "$resume" in
|
||||
'')
|
||||
if test '' != "$SIGNOFF"
|
||||
then
|
||||
LAST_SIGNED_OFF_BY=$(
|
||||
sed -ne '/^Signed-off-by: /p' \
|
||||
"$dotest/msg-clean" |
|
||||
sed -ne '$p'
|
||||
)
|
||||
ADD_SIGNOFF=$(
|
||||
test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
|
||||
test '' = "$LAST_SIGNED_OFF_BY" && echo
|
||||
echo "$SIGNOFF"
|
||||
})
|
||||
else
|
||||
ADD_SIGNOFF=
|
||||
fi
|
||||
{
|
||||
if test -s "$dotest/msg-clean"
|
||||
then
|
||||
cat "$dotest/msg-clean"
|
||||
fi
|
||||
if test '' != "$ADD_SIGNOFF"
|
||||
then
|
||||
echo "$ADD_SIGNOFF"
|
||||
fi
|
||||
} >"$dotest/final-commit"
|
||||
;;
|
||||
*)
|
||||
case "$resolved$interactive" in
|
||||
tt)
|
||||
# This is used only for interactive view option.
|
||||
git diff-index -p --cached HEAD -- >"$dotest/patch"
|
||||
;;
|
||||
esac
|
||||
esac
|
||||
|
||||
resume=
|
||||
if test "$interactive" = t
|
||||
then
|
||||
test -t 0 ||
|
||||
die "$(gettext "cannot be interactive without stdin connected to a terminal.")"
|
||||
action=again
|
||||
while test "$action" = again
|
||||
do
|
||||
gettextln "Commit Body is:"
|
||||
echo "--------------------------"
|
||||
cat "$dotest/final-commit"
|
||||
echo "--------------------------"
|
||||
# TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
|
||||
# in your translation. The program will only accept English
|
||||
# input at this point.
|
||||
gettext "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
|
||||
read reply
|
||||
case "$reply" in
|
||||
[yY]*) action=yes ;;
|
||||
[aA]*) action=yes interactive= ;;
|
||||
[nN]*) action=skip ;;
|
||||
[eE]*) git_editor "$dotest/final-commit"
|
||||
action=again ;;
|
||||
[vV]*) action=again
|
||||
git_pager "$dotest/patch" ;;
|
||||
*) action=again ;;
|
||||
esac
|
||||
done
|
||||
else
|
||||
action=yes
|
||||
fi
|
||||
|
||||
if test $action = skip
|
||||
then
|
||||
go_next
|
||||
continue
|
||||
fi
|
||||
|
||||
hook="$(git rev-parse --git-path hooks/applypatch-msg)"
|
||||
if test -x "$hook"
|
||||
then
|
||||
"$hook" "$dotest/final-commit" || stop_here $this
|
||||
fi
|
||||
|
||||
if test -f "$dotest/final-commit"
|
||||
then
|
||||
FIRSTLINE=$(sed 1q "$dotest/final-commit")
|
||||
else
|
||||
FIRSTLINE=""
|
||||
fi
|
||||
|
||||
say "$(eval_gettext "Applying: \$FIRSTLINE")"
|
||||
|
||||
case "$resolved" in
|
||||
'')
|
||||
# When we are allowed to fall back to 3-way later, don't give
|
||||
# false errors during the initial attempt.
|
||||
squelch=
|
||||
if test "$threeway" = t
|
||||
then
|
||||
squelch='>/dev/null 2>&1 '
|
||||
fi
|
||||
eval "git apply $squelch$git_apply_opt"' --index "$dotest/patch"'
|
||||
apply_status=$?
|
||||
;;
|
||||
t)
|
||||
# Resolved means the user did all the hard work, and
|
||||
# we do not have to do any patch application. Just
|
||||
# trust what the user has in the index file and the
|
||||
# working tree.
|
||||
resolved=
|
||||
git diff-index --quiet --cached HEAD -- && {
|
||||
gettextln "No changes - did you forget to use 'git add'?
|
||||
If there is nothing left to stage, chances are that something else
|
||||
already introduced the same changes; you might want to skip this patch."
|
||||
stop_here_user_resolve $this
|
||||
}
|
||||
unmerged=$(git ls-files -u)
|
||||
if test -n "$unmerged"
|
||||
then
|
||||
gettextln "You still have unmerged paths in your index
|
||||
did you forget to use 'git add'?"
|
||||
stop_here_user_resolve $this
|
||||
fi
|
||||
apply_status=0
|
||||
git rerere
|
||||
;;
|
||||
esac
|
||||
|
||||
if test $apply_status != 0 && test "$threeway" = t
|
||||
then
|
||||
if (fall_back_3way)
|
||||
then
|
||||
# Applying the patch to an earlier tree and merging the
|
||||
# result may have produced the same tree as ours.
|
||||
git diff-index --quiet --cached HEAD -- && {
|
||||
say "$(gettext "No changes -- Patch already applied.")"
|
||||
go_next
|
||||
continue
|
||||
}
|
||||
# clear apply_status -- we have successfully merged.
|
||||
apply_status=0
|
||||
fi
|
||||
fi
|
||||
if test $apply_status != 0
|
||||
then
|
||||
eval_gettextln 'Patch failed at $msgnum $FIRSTLINE'
|
||||
if test "$(git config --bool advice.amworkdir)" != false
|
||||
then
|
||||
eval_gettextln 'The copy of the patch that failed is found in:
|
||||
$dotest/patch'
|
||||
fi
|
||||
stop_here_user_resolve $this
|
||||
fi
|
||||
|
||||
hook="$(git rev-parse --git-path hooks/pre-applypatch)"
|
||||
if test -x "$hook"
|
||||
then
|
||||
"$hook" || stop_here $this
|
||||
fi
|
||||
|
||||
tree=$(git write-tree) &&
|
||||
commit=$(
|
||||
if test -n "$ignore_date"
|
||||
then
|
||||
GIT_AUTHOR_DATE=
|
||||
fi
|
||||
parent=$(git rev-parse --verify -q HEAD) ||
|
||||
say >&2 "$(gettext "applying to an empty history")"
|
||||
|
||||
if test -n "$committer_date_is_author_date"
|
||||
then
|
||||
GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
|
||||
export GIT_COMMITTER_DATE
|
||||
fi &&
|
||||
git commit-tree ${parent:+-p} $parent ${gpg_sign_opt:+"$gpg_sign_opt"} $tree \
|
||||
<"$dotest/final-commit"
|
||||
) &&
|
||||
git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
|
||||
stop_here $this
|
||||
|
||||
if test -f "$dotest/original-commit"; then
|
||||
echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten"
|
||||
fi
|
||||
|
||||
hook="$(git rev-parse --git-path hooks/post-applypatch)"
|
||||
test -x "$hook" && "$hook"
|
||||
|
||||
go_next
|
||||
done
|
||||
|
||||
if test -s "$dotest"/rewritten; then
|
||||
git notes copy --for-rewrite=rebase < "$dotest"/rewritten
|
||||
hook="$(git rev-parse --git-path hooks/post-rewrite)"
|
||||
if test -x "$hook"; then
|
||||
"$hook" rebase < "$dotest"/rewritten
|
||||
fi
|
||||
fi
|
||||
|
||||
# If am was called with --rebasing (from git-rebase--am), it's up to
|
||||
# the caller to take care of housekeeping.
|
||||
if ! test -f "$dotest/rebasing"
|
||||
then
|
||||
rm -fr "$dotest"
|
||||
git gc --auto
|
||||
fi
|
|
@ -1,302 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
OPTIONS_KEEPDASHDASH=t
|
||||
OPTIONS_SPEC="\
|
||||
git-checkout [options] [<branch>] [<paths>...]
|
||||
--
|
||||
b= create a new branch started at <branch>
|
||||
l create the new branch's reflog
|
||||
track arrange that the new branch tracks the remote branch
|
||||
f proceed even if the index or working tree is not HEAD
|
||||
m merge local modifications into the new branch
|
||||
q,quiet be quiet
|
||||
"
|
||||
SUBDIRECTORY_OK=Sometimes
|
||||
. git-sh-setup
|
||||
require_work_tree
|
||||
|
||||
old_name=HEAD
|
||||
old=$(git rev-parse --verify $old_name 2>/dev/null)
|
||||
oldbranch=$(git symbolic-ref $old_name 2>/dev/null)
|
||||
new=
|
||||
new_name=
|
||||
force=
|
||||
branch=
|
||||
track=
|
||||
newbranch=
|
||||
newbranch_log=
|
||||
merge=
|
||||
quiet=
|
||||
v=-v
|
||||
LF='
|
||||
'
|
||||
|
||||
while test $# != 0; do
|
||||
case "$1" in
|
||||
-b)
|
||||
shift
|
||||
newbranch="$1"
|
||||
[ -z "$newbranch" ] &&
|
||||
die "git checkout: -b needs a branch name"
|
||||
git show-ref --verify --quiet -- "refs/heads/$newbranch" &&
|
||||
die "git checkout: branch $newbranch already exists"
|
||||
git check-ref-format "heads/$newbranch" ||
|
||||
die "git checkout: we do not like '$newbranch' as a branch name."
|
||||
;;
|
||||
-l)
|
||||
newbranch_log=-l
|
||||
;;
|
||||
--track|--no-track)
|
||||
track="$1"
|
||||
;;
|
||||
-f)
|
||||
force=1
|
||||
;;
|
||||
-m)
|
||||
merge=1
|
||||
;;
|
||||
-q|--quiet)
|
||||
quiet=1
|
||||
v=
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
arg="$1"
|
||||
rev=$(git rev-parse --verify "$arg" 2>/dev/null)
|
||||
if rev=$(git rev-parse --verify "$rev^0" 2>/dev/null)
|
||||
then
|
||||
[ -z "$rev" ] && die "unknown flag $arg"
|
||||
new_name="$arg"
|
||||
if git show-ref --verify --quiet -- "refs/heads/$arg"
|
||||
then
|
||||
rev=$(git rev-parse --verify "refs/heads/$arg^0")
|
||||
branch="$arg"
|
||||
fi
|
||||
new="$rev"
|
||||
shift
|
||||
elif rev=$(git rev-parse --verify "$rev^{tree}" 2>/dev/null)
|
||||
then
|
||||
# checking out selected paths from a tree-ish.
|
||||
new="$rev"
|
||||
new_name="$rev^{tree}"
|
||||
shift
|
||||
fi
|
||||
[ "$1" = "--" ] && shift
|
||||
|
||||
case "$newbranch,$track" in
|
||||
,--*)
|
||||
die "git checkout: --track and --no-track require -b"
|
||||
esac
|
||||
|
||||
case "$force$merge" in
|
||||
11)
|
||||
die "git checkout: -f and -m are incompatible"
|
||||
esac
|
||||
|
||||
# The behaviour of the command with and without explicit path
|
||||
# parameters is quite different.
|
||||
#
|
||||
# Without paths, we are checking out everything in the work tree,
|
||||
# possibly switching branches. This is the traditional behaviour.
|
||||
#
|
||||
# With paths, we are _never_ switching branch, but checking out
|
||||
# the named paths from either index (when no rev is given),
|
||||
# or the named tree-ish (when rev is given).
|
||||
|
||||
if test "$#" -ge 1
|
||||
then
|
||||
hint=
|
||||
if test "$#" -eq 1
|
||||
then
|
||||
hint="
|
||||
Did you intend to checkout '$@' which can not be resolved as commit?"
|
||||
fi
|
||||
if test '' != "$newbranch$force$merge"
|
||||
then
|
||||
die "git checkout: updating paths is incompatible with switching branches/forcing$hint"
|
||||
fi
|
||||
if test '' != "$new"
|
||||
then
|
||||
# from a specific tree-ish; note that this is for
|
||||
# rescuing paths and is never meant to remove what
|
||||
# is not in the named tree-ish.
|
||||
git ls-tree --full-name -r "$new" "$@" |
|
||||
git update-index --index-info || exit $?
|
||||
fi
|
||||
|
||||
# Make sure the request is about existing paths.
|
||||
git ls-files --full-name --error-unmatch -- "$@" >/dev/null || exit
|
||||
git ls-files --full-name -- "$@" |
|
||||
(cd_to_toplevel && git checkout-index -f -u --stdin)
|
||||
|
||||
# Run a post-checkout hook -- the HEAD does not change so the
|
||||
# current HEAD is passed in for both args
|
||||
if test -x "$GIT_DIR"/hooks/post-checkout; then
|
||||
"$GIT_DIR"/hooks/post-checkout $old $old 0
|
||||
fi
|
||||
|
||||
exit $?
|
||||
else
|
||||
# Make sure we did not fall back on $arg^{tree} codepath
|
||||
# since we are not checking out from an arbitrary tree-ish,
|
||||
# but switching branches.
|
||||
if test '' != "$new"
|
||||
then
|
||||
git rev-parse --verify "$new^{commit}" >/dev/null 2>&1 ||
|
||||
die "Cannot switch branch to a non-commit."
|
||||
fi
|
||||
fi
|
||||
|
||||
# We are switching branches and checking out trees, so
|
||||
# we *NEED* to be at the toplevel.
|
||||
cd_to_toplevel
|
||||
|
||||
[ -z "$new" ] && new=$old && new_name="$old_name"
|
||||
|
||||
# If we don't have an existing branch that we're switching to,
|
||||
# and we don't have a new branch name for the target we
|
||||
# are switching to, then we are detaching our HEAD from any
|
||||
# branch. However, if "git checkout HEAD" detaches the HEAD
|
||||
# from the current branch, even though that may be logically
|
||||
# correct, it feels somewhat funny. More importantly, we do not
|
||||
# want "git checkout" or "git checkout -f" to detach HEAD.
|
||||
|
||||
detached=
|
||||
detach_warn=
|
||||
|
||||
describe_detached_head () {
|
||||
test -n "$quiet" || {
|
||||
printf >&2 "$1 "
|
||||
GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2" --
|
||||
}
|
||||
}
|
||||
|
||||
if test -z "$branch$newbranch" && test "$new_name" != "$old_name"
|
||||
then
|
||||
detached="$new"
|
||||
if test -n "$oldbranch" && test -z "$quiet"
|
||||
then
|
||||
detach_warn="Note: moving to \"$new_name\" which isn't a local branch
|
||||
If you want to create a new branch from this checkout, you may do so
|
||||
(now or later) by using -b with the checkout command again. Example:
|
||||
git checkout -b <new_branch_name>"
|
||||
fi
|
||||
elif test -z "$oldbranch" && test "$new" != "$old"
|
||||
then
|
||||
describe_detached_head 'Previous HEAD position was' "$old"
|
||||
fi
|
||||
|
||||
if [ "X$old" = X ]
|
||||
then
|
||||
if test -z "$quiet"
|
||||
then
|
||||
echo >&2 "warning: You appear to be on a branch yet to be born."
|
||||
echo >&2 "warning: Forcing checkout of $new_name."
|
||||
fi
|
||||
force=1
|
||||
fi
|
||||
|
||||
if [ "$force" ]
|
||||
then
|
||||
git read-tree $v --reset -u $new
|
||||
else
|
||||
git update-index --refresh >/dev/null
|
||||
git read-tree $v -m -u --exclude-per-directory=.gitignore $old $new || (
|
||||
case "$merge,$v" in
|
||||
,*)
|
||||
exit 1 ;;
|
||||
1,)
|
||||
;; # quiet
|
||||
*)
|
||||
echo >&2 "Falling back to 3-way merge..." ;;
|
||||
esac
|
||||
|
||||
# Match the index to the working tree, and do a three-way.
|
||||
git diff-files --name-only | git update-index --remove --stdin &&
|
||||
work=$(git write-tree) &&
|
||||
git read-tree $v --reset -u $new || exit
|
||||
|
||||
eval GITHEAD_$new='${new_name:-${branch:-$new}}' &&
|
||||
eval GITHEAD_$work=local &&
|
||||
export GITHEAD_$new GITHEAD_$work &&
|
||||
git merge-recursive $old -- $new $work
|
||||
|
||||
# Do not register the cleanly merged paths in the index yet.
|
||||
# this is not a real merge before committing, but just carrying
|
||||
# the working tree changes along.
|
||||
unmerged=$(git ls-files -u)
|
||||
git read-tree $v --reset $new
|
||||
case "$unmerged" in
|
||||
'') ;;
|
||||
*)
|
||||
(
|
||||
z40=0000000000000000000000000000000000000000
|
||||
echo "$unmerged" |
|
||||
sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /"
|
||||
echo "$unmerged"
|
||||
) | git update-index --index-info
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
)
|
||||
saved_err=$?
|
||||
if test "$saved_err" = 0 && test -z "$quiet"
|
||||
then
|
||||
git diff-index --name-status "$new"
|
||||
fi
|
||||
(exit $saved_err)
|
||||
fi
|
||||
|
||||
#
|
||||
# Switch the HEAD pointer to the new branch if we
|
||||
# checked out a branch head, and remove any potential
|
||||
# old MERGE_HEAD's (subsequent commits will clearly not
|
||||
# be based on them, since we re-set the index)
|
||||
#
|
||||
if [ "$?" -eq 0 ]; then
|
||||
if [ "$newbranch" ]; then
|
||||
git branch $track $newbranch_log "$newbranch" "$new_name" || exit
|
||||
branch="$newbranch"
|
||||
fi
|
||||
if test -n "$branch"
|
||||
then
|
||||
old_branch_name=$(expr "z$oldbranch" : 'zrefs/heads/\(.*\)')
|
||||
GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from ${old_branch_name:-$old} to $branch" HEAD "refs/heads/$branch"
|
||||
if test -n "$quiet"
|
||||
then
|
||||
true # nothing
|
||||
elif test "refs/heads/$branch" = "$oldbranch"
|
||||
then
|
||||
echo >&2 "Already on branch \"$branch\""
|
||||
else
|
||||
echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\""
|
||||
fi
|
||||
elif test -n "$detached"
|
||||
then
|
||||
old_branch_name=$(expr "z$oldbranch" : 'zrefs/heads/\(.*\)')
|
||||
git update-ref --no-deref -m "checkout: moving from ${old_branch_name:-$old} to $arg" HEAD "$detached" ||
|
||||
die "Cannot detach HEAD"
|
||||
if test -n "$detach_warn"
|
||||
then
|
||||
echo >&2 "$detach_warn"
|
||||
fi
|
||||
describe_detached_head 'HEAD is now at' HEAD
|
||||
fi
|
||||
rm -f "$GIT_DIR/MERGE_HEAD"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run a post-checkout hook
|
||||
if test -x "$GIT_DIR"/hooks/post-checkout; then
|
||||
"$GIT_DIR"/hooks/post-checkout $old $new 1
|
||||
fi
|
|
@ -1,118 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005-2006 Pavel Roskin
|
||||
#
|
||||
|
||||
OPTIONS_KEEPDASHDASH=
|
||||
OPTIONS_SPEC="\
|
||||
git-clean [options] <paths>...
|
||||
|
||||
Clean untracked files from the working directory
|
||||
|
||||
When optional <paths>... arguments are given, the paths
|
||||
affected are further limited to those that match them.
|
||||
--
|
||||
d remove directories as well
|
||||
f override clean.requireForce and clean anyway
|
||||
n don't remove anything, just show what would be done
|
||||
q be quiet, only report errors
|
||||
x remove ignored files as well
|
||||
X remove only ignored files"
|
||||
|
||||
SUBDIRECTORY_OK=Yes
|
||||
. git-sh-setup
|
||||
require_work_tree
|
||||
|
||||
ignored=
|
||||
ignoredonly=
|
||||
cleandir=
|
||||
rmf="rm -f --"
|
||||
rmrf="rm -rf --"
|
||||
rm_refuse="echo Not removing"
|
||||
echo1="echo"
|
||||
|
||||
disabled=$(git config --bool clean.requireForce)
|
||||
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-d)
|
||||
cleandir=1
|
||||
;;
|
||||
-f)
|
||||
disabled=false
|
||||
;;
|
||||
-n)
|
||||
disabled=false
|
||||
rmf="echo Would remove"
|
||||
rmrf="echo Would remove"
|
||||
rm_refuse="echo Would not remove"
|
||||
echo1=":"
|
||||
;;
|
||||
-q)
|
||||
echo1=":"
|
||||
;;
|
||||
-x)
|
||||
ignored=1
|
||||
;;
|
||||
-X)
|
||||
ignoredonly=1
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
usage # should not happen
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# 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
|
||||
|
||||
if [ "$ignored,$ignoredonly" = "1,1" ]; then
|
||||
die "-x and -X cannot be set together"
|
||||
fi
|
||||
|
||||
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"} ${excludes_file:+"$excludes_file"} \
|
||||
-- "$@" |
|
||||
while read -r file; do
|
||||
if [ -d "$file" -a ! -L "$file" ]; then
|
||||
if [ -z "$cleandir" ]; then
|
||||
$rm_refuse "$file"
|
||||
continue
|
||||
fi
|
||||
$echo1 "Removing $file"
|
||||
$rmrf "$file"
|
||||
else
|
||||
$echo1 "Removing $file"
|
||||
$rmf "$file"
|
||||
fi
|
||||
done
|
|
@ -1,525 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005, Linus Torvalds
|
||||
# Copyright (c) 2005, Junio C Hamano
|
||||
#
|
||||
# Clone a repository into a different directory that does not yet exist.
|
||||
|
||||
# See git-sh-setup why.
|
||||
unset CDPATH
|
||||
|
||||
OPTIONS_SPEC="\
|
||||
git-clone [options] [--] <repo> [<dir>]
|
||||
--
|
||||
n,no-checkout don't create a checkout
|
||||
bare create a bare repository
|
||||
naked create a bare repository
|
||||
l,local to clone from a local repository
|
||||
no-hardlinks don't use local hardlinks, always copy
|
||||
s,shared setup as a shared repository
|
||||
template= path to the template directory
|
||||
q,quiet be quiet
|
||||
reference= reference repository
|
||||
o,origin= use <name> instead of 'origin' to track upstream
|
||||
u,upload-pack= path to git-upload-pack on the remote
|
||||
depth= create a shallow clone of that depth
|
||||
|
||||
use-separate-remote compatibility, do not use
|
||||
no-separate-remote compatibility, do not use"
|
||||
|
||||
die() {
|
||||
echo >&2 "$@"
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
exec "$0" -h
|
||||
}
|
||||
|
||||
eval "$(echo "$OPTIONS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)"
|
||||
|
||||
get_repo_base() {
|
||||
(
|
||||
cd "$(/bin/pwd)" &&
|
||||
cd "$1" || cd "$1.git" &&
|
||||
{
|
||||
cd .git
|
||||
pwd
|
||||
}
|
||||
) 2>/dev/null
|
||||
}
|
||||
|
||||
if [ -n "$GIT_SSL_NO_VERIFY" -o \
|
||||
"$(git config --bool http.sslVerify)" = false ]; then
|
||||
curl_extra_args="-k"
|
||||
fi
|
||||
|
||||
http_fetch () {
|
||||
# $1 = Remote, $2 = Local
|
||||
curl -nsfL $curl_extra_args "$1" >"$2"
|
||||
curl_exit_status=$?
|
||||
case $curl_exit_status in
|
||||
126|127) exit ;;
|
||||
*) return $curl_exit_status ;;
|
||||
esac
|
||||
}
|
||||
|
||||
clone_dumb_http () {
|
||||
# $1 - remote, $2 - local
|
||||
cd "$2" &&
|
||||
clone_tmp="$GIT_DIR/clone-tmp" &&
|
||||
mkdir -p "$clone_tmp" || exit 1
|
||||
if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
|
||||
"$(git config --bool http.noEPSV)" = true ]; then
|
||||
curl_extra_args="${curl_extra_args} --disable-epsv"
|
||||
fi
|
||||
http_fetch "$1/info/refs" "$clone_tmp/refs" ||
|
||||
die "Cannot get remote repository information.
|
||||
Perhaps git-update-server-info needs to be run there?"
|
||||
test "z$quiet" = z && v=-v || v=
|
||||
while read sha1 refname
|
||||
do
|
||||
name=$(expr "z$refname" : 'zrefs/\(.*\)') &&
|
||||
case "$name" in
|
||||
*^*) continue;;
|
||||
esac
|
||||
case "$bare,$name" in
|
||||
yes,* | ,heads/* | ,tags/*) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
if test -n "$use_separate_remote" &&
|
||||
branch_name=$(expr "z$name" : 'zheads/\(.*\)')
|
||||
then
|
||||
tname="remotes/$origin/$branch_name"
|
||||
else
|
||||
tname=$name
|
||||
fi
|
||||
git-http-fetch $v -a -w "$tname" "$sha1" "$1" || exit 1
|
||||
done <"$clone_tmp/refs"
|
||||
rm -fr "$clone_tmp"
|
||||
http_fetch "$1/HEAD" "$GIT_DIR/REMOTE_HEAD" ||
|
||||
rm -f "$GIT_DIR/REMOTE_HEAD"
|
||||
if test -f "$GIT_DIR/REMOTE_HEAD"; then
|
||||
head_sha1=$(cat "$GIT_DIR/REMOTE_HEAD")
|
||||
case "$head_sha1" in
|
||||
'ref: refs/'*)
|
||||
;;
|
||||
*)
|
||||
git-http-fetch $v -a "$head_sha1" "$1" ||
|
||||
rm -f "$GIT_DIR/REMOTE_HEAD"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
quiet=
|
||||
local=no
|
||||
use_local_hardlink=yes
|
||||
local_shared=no
|
||||
unset template
|
||||
no_checkout=
|
||||
upload_pack=
|
||||
bare=
|
||||
reference=
|
||||
origin=
|
||||
origin_override=
|
||||
use_separate_remote=t
|
||||
depth=
|
||||
no_progress=
|
||||
local_explicitly_asked_for=
|
||||
test -t 1 || no_progress=--no-progress
|
||||
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-n|--no-checkout)
|
||||
no_checkout=yes ;;
|
||||
--naked|--bare)
|
||||
bare=yes ;;
|
||||
-l|--local)
|
||||
local_explicitly_asked_for=yes
|
||||
use_local_hardlink=yes
|
||||
;;
|
||||
--no-hardlinks)
|
||||
use_local_hardlink=no ;;
|
||||
-s|--shared)
|
||||
local_shared=yes ;;
|
||||
--template)
|
||||
shift; template="--template=$1" ;;
|
||||
-q|--quiet)
|
||||
quiet=-q ;;
|
||||
--use-separate-remote|--no-separate-remote)
|
||||
die "clones are always made with separate-remote layout" ;;
|
||||
--reference)
|
||||
shift; reference="$1" ;;
|
||||
-o|--origin)
|
||||
shift;
|
||||
case "$1" in
|
||||
'')
|
||||
usage ;;
|
||||
*/*)
|
||||
die "'$1' is not suitable for an origin name"
|
||||
esac
|
||||
git check-ref-format "heads/$1" ||
|
||||
die "'$1' is not suitable for a branch name"
|
||||
test -z "$origin_override" ||
|
||||
die "Do not give more than one --origin options."
|
||||
origin_override=yes
|
||||
origin="$1"
|
||||
;;
|
||||
-u|--upload-pack)
|
||||
shift
|
||||
upload_pack="--upload-pack=$1" ;;
|
||||
--depth)
|
||||
shift
|
||||
depth="--depth=$1" ;;
|
||||
--)
|
||||
shift
|
||||
break ;;
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
repo="$1"
|
||||
test -n "$repo" ||
|
||||
die 'you must specify a repository to clone.'
|
||||
|
||||
# --bare implies --no-checkout and --no-separate-remote
|
||||
if test yes = "$bare"
|
||||
then
|
||||
if test yes = "$origin_override"
|
||||
then
|
||||
die '--bare and --origin $origin options are incompatible.'
|
||||
fi
|
||||
no_checkout=yes
|
||||
use_separate_remote=
|
||||
fi
|
||||
|
||||
if test -z "$origin"
|
||||
then
|
||||
origin=origin
|
||||
fi
|
||||
|
||||
# Turn the source into an absolute path if
|
||||
# it is local
|
||||
if base=$(get_repo_base "$repo"); then
|
||||
repo="$base"
|
||||
if test -z "$depth"
|
||||
then
|
||||
local=yes
|
||||
fi
|
||||
elif test -f "$repo"
|
||||
then
|
||||
case "$repo" in /*) ;; *) repo="$PWD/$repo" ;; esac
|
||||
fi
|
||||
|
||||
# Decide the directory name of the new repository
|
||||
if test -n "$2"
|
||||
then
|
||||
dir="$2"
|
||||
test $# = 2 || die "excess parameter to git-clone"
|
||||
else
|
||||
# Derive one from the repository name
|
||||
# Try using "humanish" part of source repo if user didn't specify one
|
||||
if test -f "$repo"
|
||||
then
|
||||
# Cloning from a bundle
|
||||
dir=$(echo "$repo" | sed -e 's|/*\.bundle$||' -e 's|.*/||g')
|
||||
else
|
||||
dir=$(echo "$repo" |
|
||||
sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
|
||||
fi
|
||||
fi
|
||||
|
||||
[ -e "$dir" ] && die "destination directory '$dir' already exists."
|
||||
[ yes = "$bare" ] && unset GIT_WORK_TREE
|
||||
[ -n "$GIT_WORK_TREE" ] && [ -e "$GIT_WORK_TREE" ] &&
|
||||
die "working tree '$GIT_WORK_TREE' already exists."
|
||||
D=
|
||||
W=
|
||||
cleanup() {
|
||||
test -z "$D" && rm -rf "$dir"
|
||||
test -z "$W" && test -n "$GIT_WORK_TREE" && rm -rf "$GIT_WORK_TREE"
|
||||
cd ..
|
||||
test -n "$D" && rm -rf "$D"
|
||||
test -n "$W" && rm -rf "$W"
|
||||
exit $err
|
||||
}
|
||||
trap 'err=$?; cleanup' 0
|
||||
mkdir -p "$dir" && D=$(cd "$dir" && pwd) || usage
|
||||
test -n "$GIT_WORK_TREE" && mkdir -p "$GIT_WORK_TREE" &&
|
||||
W=$(cd "$GIT_WORK_TREE" && pwd) && GIT_WORK_TREE="$W" && export GIT_WORK_TREE
|
||||
if test yes = "$bare" || test -n "$GIT_WORK_TREE"; then
|
||||
GIT_DIR="$D"
|
||||
else
|
||||
GIT_DIR="$D/.git"
|
||||
fi &&
|
||||
export GIT_DIR &&
|
||||
GIT_CONFIG="$GIT_DIR/config" git-init $quiet ${template+"$template"} || usage
|
||||
|
||||
if test -n "$bare"
|
||||
then
|
||||
GIT_CONFIG="$GIT_DIR/config" git config core.bare true
|
||||
fi
|
||||
|
||||
if test -n "$reference"
|
||||
then
|
||||
ref_git=
|
||||
if test -d "$reference"
|
||||
then
|
||||
if test -d "$reference/.git/objects"
|
||||
then
|
||||
ref_git="$reference/.git"
|
||||
elif test -d "$reference/objects"
|
||||
then
|
||||
ref_git="$reference"
|
||||
fi
|
||||
fi
|
||||
if test -n "$ref_git"
|
||||
then
|
||||
ref_git=$(cd "$ref_git" && pwd)
|
||||
echo "$ref_git/objects" >"$GIT_DIR/objects/info/alternates"
|
||||
(
|
||||
GIT_DIR="$ref_git" git for-each-ref \
|
||||
--format='%(objectname) %(*objectname)'
|
||||
) |
|
||||
while read a b
|
||||
do
|
||||
test -z "$a" ||
|
||||
git update-ref "refs/reference-tmp/$a" "$a"
|
||||
test -z "$b" ||
|
||||
git update-ref "refs/reference-tmp/$b" "$b"
|
||||
done
|
||||
else
|
||||
die "reference repository '$reference' is not a local directory."
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f "$GIT_DIR/CLONE_HEAD"
|
||||
|
||||
# We do local magic only when the user tells us to.
|
||||
case "$local" in
|
||||
yes)
|
||||
( cd "$repo/objects" ) ||
|
||||
die "cannot chdir to local '$repo/objects'."
|
||||
|
||||
if test "$local_shared" = yes
|
||||
then
|
||||
mkdir -p "$GIT_DIR/objects/info"
|
||||
echo "$repo/objects" >>"$GIT_DIR/objects/info/alternates"
|
||||
else
|
||||
cpio_quiet_flag=""
|
||||
cpio --help 2>&1 | grep -- --quiet >/dev/null && \
|
||||
cpio_quiet_flag=--quiet
|
||||
l= &&
|
||||
if test "$use_local_hardlink" = yes
|
||||
then
|
||||
# See if we can hardlink and drop "l" if not.
|
||||
sample_file=$(cd "$repo" && \
|
||||
find objects -type f -print | sed -e 1q)
|
||||
# objects directory should not be empty because
|
||||
# we are cloning!
|
||||
test -f "$repo/$sample_file" ||
|
||||
die "fatal: cannot clone empty repository"
|
||||
if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null
|
||||
then
|
||||
rm -f "$GIT_DIR/objects/sample"
|
||||
l=l
|
||||
elif test -n "$local_explicitly_asked_for"
|
||||
then
|
||||
echo >&2 "Warning: -l asked but cannot hardlink to $repo"
|
||||
fi
|
||||
fi &&
|
||||
cd "$repo" &&
|
||||
# Create dirs using umask and permissions and destination
|
||||
find objects -type d -print | (cd "$GIT_DIR" && xargs mkdir -p) &&
|
||||
# Copy existing 0444 permissions on content
|
||||
find objects ! -type d -print | cpio $cpio_quiet_flag -pumd$l "$GIT_DIR/" || \
|
||||
exit 1
|
||||
fi
|
||||
git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1
|
||||
;;
|
||||
*)
|
||||
case "$repo" in
|
||||
rsync://*)
|
||||
case "$depth" in
|
||||
"") ;;
|
||||
*) die "shallow over rsync not supported" ;;
|
||||
esac
|
||||
rsync $quiet -av --ignore-existing \
|
||||
--exclude info "$repo/objects/" "$GIT_DIR/objects/" ||
|
||||
exit
|
||||
# Look at objects/info/alternates for rsync -- http will
|
||||
# support it natively and git native ones will do it on the
|
||||
# remote end. Not having that file is not a crime.
|
||||
rsync -q "$repo/objects/info/alternates" \
|
||||
"$GIT_DIR/TMP_ALT" 2>/dev/null ||
|
||||
rm -f "$GIT_DIR/TMP_ALT"
|
||||
if test -f "$GIT_DIR/TMP_ALT"
|
||||
then
|
||||
( cd "$D" &&
|
||||
. git-parse-remote &&
|
||||
resolve_alternates "$repo" <"$GIT_DIR/TMP_ALT" ) |
|
||||
while read alt
|
||||
do
|
||||
case "$alt" in 'bad alternate: '*) die "$alt";; esac
|
||||
case "$quiet" in
|
||||
'') echo >&2 "Getting alternate: $alt" ;;
|
||||
esac
|
||||
rsync $quiet -av --ignore-existing \
|
||||
--exclude info "$alt" "$GIT_DIR/objects" || exit
|
||||
done
|
||||
rm -f "$GIT_DIR/TMP_ALT"
|
||||
fi
|
||||
git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1
|
||||
;;
|
||||
https://*|http://*|ftp://*)
|
||||
case "$depth" in
|
||||
"") ;;
|
||||
*) die "shallow over http or ftp not supported" ;;
|
||||
esac
|
||||
if test -z "@@NO_CURL@@"
|
||||
then
|
||||
clone_dumb_http "$repo" "$D"
|
||||
else
|
||||
die "http transport not supported, rebuild Git with curl support"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
if [ -f "$repo" ] ; then
|
||||
git bundle unbundle "$repo" > "$GIT_DIR/CLONE_HEAD" ||
|
||||
die "unbundle from '$repo' failed."
|
||||
else
|
||||
case "$upload_pack" in
|
||||
'') git-fetch-pack --all -k $quiet $depth $no_progress "$repo";;
|
||||
*) git-fetch-pack --all -k \
|
||||
$quiet "$upload_pack" $depth $no_progress "$repo" ;;
|
||||
esac >"$GIT_DIR/CLONE_HEAD" ||
|
||||
die "fetch-pack from '$repo' failed."
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
test -d "$GIT_DIR/refs/reference-tmp" && rm -fr "$GIT_DIR/refs/reference-tmp"
|
||||
|
||||
if test -f "$GIT_DIR/CLONE_HEAD"
|
||||
then
|
||||
# Read git-fetch-pack -k output and store the remote branches.
|
||||
if [ -n "$use_separate_remote" ]
|
||||
then
|
||||
branch_top="remotes/$origin"
|
||||
else
|
||||
branch_top="heads"
|
||||
fi
|
||||
tag_top="tags"
|
||||
while read sha1 name
|
||||
do
|
||||
case "$name" in
|
||||
*'^{}')
|
||||
continue ;;
|
||||
HEAD)
|
||||
destname="REMOTE_HEAD" ;;
|
||||
refs/heads/*)
|
||||
destname="refs/$branch_top/${name#refs/heads/}" ;;
|
||||
refs/tags/*)
|
||||
destname="refs/$tag_top/${name#refs/tags/}" ;;
|
||||
*)
|
||||
continue ;;
|
||||
esac
|
||||
git update-ref -m "clone: from $repo" "$destname" "$sha1" ""
|
||||
done < "$GIT_DIR/CLONE_HEAD"
|
||||
fi
|
||||
|
||||
if test -n "$W"; then
|
||||
cd "$W" || exit
|
||||
else
|
||||
cd "$D" || exit
|
||||
fi
|
||||
|
||||
if test -z "$bare"
|
||||
then
|
||||
# a non-bare repository is always in separate-remote layout
|
||||
remote_top="refs/remotes/$origin"
|
||||
head_sha1=
|
||||
test ! -r "$GIT_DIR/REMOTE_HEAD" || head_sha1=$(cat "$GIT_DIR/REMOTE_HEAD")
|
||||
case "$head_sha1" in
|
||||
'ref: refs/'*)
|
||||
# Uh-oh, the remote told us (http transport done against
|
||||
# new style repository with a symref HEAD).
|
||||
# Ideally we should skip the guesswork but for now
|
||||
# opt for minimum change.
|
||||
head_sha1=$(expr "z$head_sha1" : 'zref: refs/heads/\(.*\)')
|
||||
head_sha1=$(cat "$GIT_DIR/$remote_top/$head_sha1")
|
||||
;;
|
||||
esac
|
||||
|
||||
# The name under $remote_top the remote HEAD seems to point at.
|
||||
head_points_at=$(
|
||||
(
|
||||
test -f "$GIT_DIR/$remote_top/master" && echo "master"
|
||||
cd "$GIT_DIR/$remote_top" &&
|
||||
find . -type f -print | sed -e 's/^\.\///'
|
||||
) | (
|
||||
done=f
|
||||
while read name
|
||||
do
|
||||
test t = $done && continue
|
||||
branch_tip=$(cat "$GIT_DIR/$remote_top/$name")
|
||||
if test "$head_sha1" = "$branch_tip"
|
||||
then
|
||||
echo "$name"
|
||||
done=t
|
||||
fi
|
||||
done
|
||||
)
|
||||
)
|
||||
|
||||
# Upstream URL
|
||||
git config remote."$origin".url "$repo" &&
|
||||
|
||||
# Set up the mappings to track the remote branches.
|
||||
git config remote."$origin".fetch \
|
||||
"+refs/heads/*:$remote_top/*" '^$' &&
|
||||
|
||||
# Write out remote.$origin config, and update our "$head_points_at".
|
||||
case "$head_points_at" in
|
||||
?*)
|
||||
# Local default branch
|
||||
git symbolic-ref HEAD "refs/heads/$head_points_at" &&
|
||||
|
||||
# Tracking branch for the primary branch at the remote.
|
||||
git update-ref HEAD "$head_sha1" &&
|
||||
|
||||
rm -f "refs/remotes/$origin/HEAD"
|
||||
git symbolic-ref "refs/remotes/$origin/HEAD" \
|
||||
"refs/remotes/$origin/$head_points_at" &&
|
||||
|
||||
git config branch."$head_points_at".remote "$origin" &&
|
||||
git config branch."$head_points_at".merge "refs/heads/$head_points_at"
|
||||
;;
|
||||
'')
|
||||
if test -z "$head_sha1"
|
||||
then
|
||||
# Source had nonexistent ref in HEAD
|
||||
echo >&2 "Warning: Remote HEAD refers to nonexistent ref, unable to checkout."
|
||||
no_checkout=t
|
||||
else
|
||||
# Source had detached HEAD pointing nowhere
|
||||
git update-ref --no-deref HEAD "$head_sha1" &&
|
||||
rm -f "refs/remotes/$origin/HEAD"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$no_checkout" in
|
||||
'')
|
||||
test "z$quiet" = z && test "z$no_progress" = z && v=-v || v=
|
||||
git read-tree -m -u $v HEAD HEAD
|
||||
esac
|
||||
fi
|
||||
rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD"
|
||||
|
||||
trap - 0
|
|
@ -1,639 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Linus Torvalds
|
||||
# Copyright (c) 2006 Junio C Hamano
|
||||
|
||||
USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]'
|
||||
SUBDIRECTORY_OK=Yes
|
||||
OPTIONS_SPEC=
|
||||
. git-sh-setup
|
||||
require_work_tree
|
||||
|
||||
git rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
|
||||
|
||||
case "$0" in
|
||||
*status)
|
||||
status_only=t
|
||||
;;
|
||||
*commit)
|
||||
status_only=
|
||||
;;
|
||||
esac
|
||||
|
||||
refuse_partial () {
|
||||
echo >&2 "$1"
|
||||
echo >&2 "You might have meant to say 'git commit -i paths...', perhaps?"
|
||||
exit 1
|
||||
}
|
||||
|
||||
TMP_INDEX=
|
||||
THIS_INDEX="${GIT_INDEX_FILE:-$GIT_DIR/index}"
|
||||
NEXT_INDEX="$GIT_DIR/next-index$$"
|
||||
rm -f "$NEXT_INDEX"
|
||||
save_index () {
|
||||
cp -p "$THIS_INDEX" "$NEXT_INDEX"
|
||||
}
|
||||
|
||||
run_status () {
|
||||
# If TMP_INDEX is defined, that means we are doing
|
||||
# "--only" partial commit, and that index file is used
|
||||
# to build the tree for the commit. Otherwise, if
|
||||
# NEXT_INDEX exists, that is the index file used to
|
||||
# make the commit. Otherwise we are using as-is commit
|
||||
# so the regular index file is what we use to compare.
|
||||
if test '' != "$TMP_INDEX"
|
||||
then
|
||||
GIT_INDEX_FILE="$TMP_INDEX"
|
||||
export GIT_INDEX_FILE
|
||||
elif test -f "$NEXT_INDEX"
|
||||
then
|
||||
GIT_INDEX_FILE="$NEXT_INDEX"
|
||||
export GIT_INDEX_FILE
|
||||
fi
|
||||
|
||||
if test "$status_only" = "t" || test "$use_status_color" = "t"; then
|
||||
color=
|
||||
else
|
||||
color=--nocolor
|
||||
fi
|
||||
git runstatus ${color} \
|
||||
${verbose:+--verbose} \
|
||||
${amend:+--amend} \
|
||||
${untracked_files:+--untracked}
|
||||
}
|
||||
|
||||
trap '
|
||||
test -z "$TMP_INDEX" || {
|
||||
test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
|
||||
}
|
||||
rm -f "$NEXT_INDEX"
|
||||
' 0
|
||||
|
||||
################################################################
|
||||
# Command line argument parsing and sanity checking
|
||||
|
||||
all=
|
||||
also=
|
||||
allow_empty=f
|
||||
interactive=
|
||||
only=
|
||||
logfile=
|
||||
use_commit=
|
||||
amend=
|
||||
edit_flag=
|
||||
no_edit=
|
||||
log_given=
|
||||
log_message=
|
||||
verify=t
|
||||
quiet=
|
||||
verbose=
|
||||
signoff=
|
||||
force_author=
|
||||
only_include_assumed=
|
||||
untracked_files=
|
||||
templatefile="$(git config commit.template)"
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-F|--F|-f|--f|--fi|--fil|--file)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
no_edit=t
|
||||
log_given=t$log_given
|
||||
logfile="$1"
|
||||
;;
|
||||
-F*|-f*)
|
||||
no_edit=t
|
||||
log_given=t$log_given
|
||||
logfile="${1#-[Ff]}"
|
||||
;;
|
||||
--F=*|--f=*|--fi=*|--fil=*|--file=*)
|
||||
no_edit=t
|
||||
log_given=t$log_given
|
||||
logfile="${1#*=}"
|
||||
;;
|
||||
-a|--a|--al|--all)
|
||||
all=t
|
||||
;;
|
||||
--allo|--allow|--allow-|--allow-e|--allow-em|--allow-emp|\
|
||||
--allow-empt|--allow-empty)
|
||||
allow_empty=t
|
||||
;;
|
||||
--au=*|--aut=*|--auth=*|--autho=*|--author=*)
|
||||
force_author="${1#*=}"
|
||||
;;
|
||||
--au|--aut|--auth|--autho|--author)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
force_author="$1"
|
||||
;;
|
||||
-e|--e|--ed|--edi|--edit)
|
||||
edit_flag=t
|
||||
;;
|
||||
-i|--i|--in|--inc|--incl|--inclu|--includ|--include)
|
||||
also=t
|
||||
;;
|
||||
--int|--inte|--inter|--intera|--interac|--interact|--interacti|\
|
||||
--interactiv|--interactive)
|
||||
interactive=t
|
||||
;;
|
||||
-o|--o|--on|--onl|--only)
|
||||
only=t
|
||||
;;
|
||||
-m|--m|--me|--mes|--mess|--messa|--messag|--message)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=m$log_given
|
||||
log_message="${log_message:+${log_message}
|
||||
|
||||
}$1"
|
||||
no_edit=t
|
||||
;;
|
||||
-m*)
|
||||
log_given=m$log_given
|
||||
log_message="${log_message:+${log_message}
|
||||
|
||||
}${1#-m}"
|
||||
no_edit=t
|
||||
;;
|
||||
--m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
|
||||
log_given=m$log_given
|
||||
log_message="${log_message:+${log_message}
|
||||
|
||||
}${1#*=}"
|
||||
no_edit=t
|
||||
;;
|
||||
-n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\
|
||||
--no-verify)
|
||||
verify=
|
||||
;;
|
||||
--a|--am|--ame|--amen|--amend)
|
||||
amend=t
|
||||
use_commit=HEAD
|
||||
;;
|
||||
-c)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=t$log_given
|
||||
use_commit="$1"
|
||||
no_edit=
|
||||
;;
|
||||
--ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
|
||||
--reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
|
||||
--reedit-messag=*|--reedit-message=*)
|
||||
log_given=t$log_given
|
||||
use_commit="${1#*=}"
|
||||
no_edit=
|
||||
;;
|
||||
--ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
|
||||
--reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\
|
||||
--reedit-message)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=t$log_given
|
||||
use_commit="$1"
|
||||
no_edit=
|
||||
;;
|
||||
-C)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=t$log_given
|
||||
use_commit="$1"
|
||||
no_edit=t
|
||||
;;
|
||||
--reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
|
||||
--reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
|
||||
--reuse-message=*)
|
||||
log_given=t$log_given
|
||||
use_commit="${1#*=}"
|
||||
no_edit=t
|
||||
;;
|
||||
--reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
|
||||
--reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
log_given=t$log_given
|
||||
use_commit="$1"
|
||||
no_edit=t
|
||||
;;
|
||||
-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
|
||||
signoff=t
|
||||
;;
|
||||
-t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template)
|
||||
case "$#" in 1) usage ;; esac
|
||||
shift
|
||||
templatefile="$1"
|
||||
no_edit=
|
||||
;;
|
||||
-q|--q|--qu|--qui|--quie|--quiet)
|
||||
quiet=t
|
||||
;;
|
||||
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
|
||||
verbose=t
|
||||
;;
|
||||
-u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\
|
||||
--untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\
|
||||
--untracked-file|--untracked-files)
|
||||
untracked_files=t
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
case "$edit_flag" in t) no_edit= ;; esac
|
||||
|
||||
################################################################
|
||||
# Sanity check options
|
||||
|
||||
case "$amend,$initial_commit" in
|
||||
t,t)
|
||||
die "You do not have anything to amend." ;;
|
||||
t,)
|
||||
if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
|
||||
die "You are in the middle of a merge -- cannot amend."
|
||||
fi ;;
|
||||
esac
|
||||
|
||||
case "$log_given" in
|
||||
tt*)
|
||||
die "Only one of -c/-C/-F can be used." ;;
|
||||
*tm*|*mt*)
|
||||
die "Option -m cannot be combined with -c/-C/-F." ;;
|
||||
esac
|
||||
|
||||
case "$#,$also,$only,$amend" in
|
||||
*,t,t,*)
|
||||
die "Only one of --include/--only can be used." ;;
|
||||
0,t,,* | 0,,t,)
|
||||
die "No paths with --include/--only does not make sense." ;;
|
||||
0,,t,t)
|
||||
only_include_assumed="# Clever... amending the last one with dirty index." ;;
|
||||
0,,,*)
|
||||
;;
|
||||
*,,,*)
|
||||
only_include_assumed="# Explicit paths specified without -i or -o; assuming --only paths..."
|
||||
also=
|
||||
;;
|
||||
esac
|
||||
unset only
|
||||
case "$all,$interactive,$also,$#" in
|
||||
*t,*t,*)
|
||||
die "Cannot use -a, --interactive or -i at the same time." ;;
|
||||
t,,,[1-9]*)
|
||||
die "Paths with -a does not make sense." ;;
|
||||
,t,,[1-9]*)
|
||||
die "Paths with --interactive does not make sense." ;;
|
||||
,,t,0)
|
||||
die "No paths with -i does not make sense." ;;
|
||||
esac
|
||||
|
||||
if test ! -z "$templatefile" && test -z "$log_given"
|
||||
then
|
||||
if test ! -f "$templatefile"
|
||||
then
|
||||
die "Commit template file does not exist."
|
||||
fi
|
||||
fi
|
||||
|
||||
################################################################
|
||||
# Prepare index to have a tree to be committed
|
||||
|
||||
case "$all,$also" in
|
||||
t,)
|
||||
if test ! -f "$THIS_INDEX"
|
||||
then
|
||||
die 'nothing to commit (use "git add file1 file2" to include for commit)'
|
||||
fi
|
||||
save_index &&
|
||||
(
|
||||
cd_to_toplevel &&
|
||||
GIT_INDEX_FILE="$NEXT_INDEX" &&
|
||||
export GIT_INDEX_FILE &&
|
||||
git diff-files --name-only -z |
|
||||
git update-index --remove -z --stdin
|
||||
) || exit
|
||||
;;
|
||||
,t)
|
||||
save_index &&
|
||||
git ls-files --error-unmatch -- "$@" >/dev/null || exit
|
||||
|
||||
git diff-files --name-only -z -- "$@" |
|
||||
(
|
||||
cd_to_toplevel &&
|
||||
GIT_INDEX_FILE="$NEXT_INDEX" &&
|
||||
export GIT_INDEX_FILE &&
|
||||
git update-index --remove -z --stdin
|
||||
) || exit
|
||||
;;
|
||||
,)
|
||||
if test "$interactive" = t; then
|
||||
git add --interactive || exit
|
||||
fi
|
||||
case "$#" in
|
||||
0)
|
||||
;; # commit as-is
|
||||
*)
|
||||
if test -f "$GIT_DIR/MERGE_HEAD"
|
||||
then
|
||||
refuse_partial "Cannot do a partial commit during a merge."
|
||||
fi
|
||||
|
||||
TMP_INDEX="$GIT_DIR/tmp-index$$"
|
||||
W=
|
||||
test -z "$initial_commit" && W=--with-tree=HEAD
|
||||
commit_only=$(git ls-files --error-unmatch $W -- "$@") || exit
|
||||
|
||||
# Build a temporary index and update the real index
|
||||
# the same way.
|
||||
if test -z "$initial_commit"
|
||||
then
|
||||
GIT_INDEX_FILE="$THIS_INDEX" \
|
||||
git read-tree --index-output="$TMP_INDEX" -i -m HEAD
|
||||
else
|
||||
rm -f "$TMP_INDEX"
|
||||
fi || exit
|
||||
|
||||
printf '%s\n' "$commit_only" |
|
||||
GIT_INDEX_FILE="$TMP_INDEX" \
|
||||
git update-index --add --remove --stdin &&
|
||||
|
||||
save_index &&
|
||||
printf '%s\n' "$commit_only" |
|
||||
(
|
||||
GIT_INDEX_FILE="$NEXT_INDEX"
|
||||
export GIT_INDEX_FILE
|
||||
git update-index --add --remove --stdin
|
||||
) || exit
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
################################################################
|
||||
# If we do as-is commit, the index file will be THIS_INDEX,
|
||||
# otherwise NEXT_INDEX after we make this commit. We leave
|
||||
# the index as is if we abort.
|
||||
|
||||
if test -f "$NEXT_INDEX"
|
||||
then
|
||||
USE_INDEX="$NEXT_INDEX"
|
||||
else
|
||||
USE_INDEX="$THIS_INDEX"
|
||||
fi
|
||||
|
||||
case "$status_only" in
|
||||
t)
|
||||
# This will silently fail in a read-only repository, which is
|
||||
# what we want.
|
||||
GIT_INDEX_FILE="$USE_INDEX" git update-index -q --unmerged --refresh
|
||||
run_status
|
||||
exit $?
|
||||
;;
|
||||
'')
|
||||
GIT_INDEX_FILE="$USE_INDEX" git update-index -q --refresh || exit
|
||||
;;
|
||||
esac
|
||||
|
||||
################################################################
|
||||
# Grab commit message, write out tree and make commit.
|
||||
|
||||
if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit
|
||||
then
|
||||
GIT_INDEX_FILE="${TMP_INDEX:-${USE_INDEX}}" "$GIT_DIR"/hooks/pre-commit \
|
||||
|| exit
|
||||
fi
|
||||
|
||||
if test "$log_message" != ''
|
||||
then
|
||||
printf '%s\n' "$log_message"
|
||||
elif test "$logfile" != ""
|
||||
then
|
||||
if test "$logfile" = -
|
||||
then
|
||||
test -t 0 &&
|
||||
echo >&2 "(reading log message from standard input)"
|
||||
cat
|
||||
else
|
||||
cat <"$logfile"
|
||||
fi
|
||||
elif test "$use_commit" != ""
|
||||
then
|
||||
encoding=$(git config i18n.commitencoding || echo UTF-8)
|
||||
git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
|
||||
sed -e '1,/^$/d' -e 's/^ //'
|
||||
elif test -f "$GIT_DIR/MERGE_MSG"
|
||||
then
|
||||
cat "$GIT_DIR/MERGE_MSG"
|
||||
elif test -f "$GIT_DIR/SQUASH_MSG"
|
||||
then
|
||||
cat "$GIT_DIR/SQUASH_MSG"
|
||||
elif test "$templatefile" != ""
|
||||
then
|
||||
cat "$templatefile"
|
||||
fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG
|
||||
|
||||
case "$signoff" in
|
||||
t)
|
||||
sign=$(git var GIT_COMMITTER_IDENT | sed -e '
|
||||
s/>.*/>/
|
||||
s/^/Signed-off-by: /
|
||||
')
|
||||
blank_before_signoff=
|
||||
tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
|
||||
grep 'Signed-off-by:' >/dev/null || blank_before_signoff='
|
||||
'
|
||||
tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
|
||||
grep "$sign"$ >/dev/null ||
|
||||
printf '%s%s\n' "$blank_before_signoff" "$sign" \
|
||||
>>"$GIT_DIR"/COMMIT_EDITMSG
|
||||
;;
|
||||
esac
|
||||
|
||||
if test -f "$GIT_DIR/MERGE_HEAD" && test -z "$no_edit"; then
|
||||
echo "#"
|
||||
echo "# It looks like you may be committing a MERGE."
|
||||
echo "# If this is not correct, please remove the file"
|
||||
printf '%s\n' "# $GIT_DIR/MERGE_HEAD"
|
||||
echo "# and try again"
|
||||
echo "#"
|
||||
fi >>"$GIT_DIR"/COMMIT_EDITMSG
|
||||
|
||||
# Author
|
||||
if test '' != "$use_commit"
|
||||
then
|
||||
eval "$(get_author_ident_from_commit "$use_commit")"
|
||||
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
|
||||
fi
|
||||
if test '' != "$force_author"
|
||||
then
|
||||
GIT_AUTHOR_NAME=$(expr "z$force_author" : 'z\(.*[^ ]\) *<.*') &&
|
||||
GIT_AUTHOR_EMAIL=$(expr "z$force_author" : '.*\(<.*\)') &&
|
||||
test '' != "$GIT_AUTHOR_NAME" &&
|
||||
test '' != "$GIT_AUTHOR_EMAIL" ||
|
||||
die "malformed --author parameter"
|
||||
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
|
||||
fi
|
||||
|
||||
PARENTS="-p HEAD"
|
||||
if test -z "$initial_commit"
|
||||
then
|
||||
rloga='commit'
|
||||
if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
|
||||
rloga='commit (merge)'
|
||||
PARENTS="-p HEAD "$(sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD")
|
||||
elif test -n "$amend"; then
|
||||
rloga='commit (amend)'
|
||||
PARENTS=$(git cat-file commit HEAD |
|
||||
sed -n -e '/^$/q' -e 's/^parent /-p /p')
|
||||
fi
|
||||
current="$(git rev-parse --verify HEAD)"
|
||||
else
|
||||
if [ -z "$(git ls-files)" ]; then
|
||||
echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)'
|
||||
exit 1
|
||||
fi
|
||||
PARENTS=""
|
||||
rloga='commit (initial)'
|
||||
current=''
|
||||
fi
|
||||
set_reflog_action "$rloga"
|
||||
|
||||
if test -z "$no_edit"
|
||||
then
|
||||
{
|
||||
echo ""
|
||||
echo "# Please enter the commit message for your changes."
|
||||
echo "# (Comment lines starting with '#' will not be included)"
|
||||
test -z "$only_include_assumed" || echo "$only_include_assumed"
|
||||
run_status
|
||||
} >>"$GIT_DIR"/COMMIT_EDITMSG
|
||||
else
|
||||
# we need to check if there is anything to commit
|
||||
run_status >/dev/null
|
||||
fi
|
||||
case "$allow_empty,$?,$PARENTS" in
|
||||
t,* | ?,0,* | ?,*,-p' '?*-p' '?*)
|
||||
# an explicit --allow-empty, or a merge commit can record the
|
||||
# same tree as its parent. Otherwise having commitable paths
|
||||
# is required.
|
||||
;;
|
||||
*)
|
||||
rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
|
||||
use_status_color=t
|
||||
run_status
|
||||
exit 1
|
||||
esac
|
||||
|
||||
case "$no_edit" in
|
||||
'')
|
||||
git var GIT_AUTHOR_IDENT > /dev/null || die
|
||||
git var GIT_COMMITTER_IDENT > /dev/null || die
|
||||
git_editor "$GIT_DIR/COMMIT_EDITMSG"
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$verify" in
|
||||
t)
|
||||
if test -x "$GIT_DIR"/hooks/commit-msg
|
||||
then
|
||||
"$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit
|
||||
fi
|
||||
esac
|
||||
|
||||
if test -z "$no_edit"
|
||||
then
|
||||
sed -e '
|
||||
/^diff --git a\/.*/{
|
||||
s///
|
||||
q
|
||||
}
|
||||
/^#/d
|
||||
' "$GIT_DIR"/COMMIT_EDITMSG
|
||||
else
|
||||
cat "$GIT_DIR"/COMMIT_EDITMSG
|
||||
fi |
|
||||
git stripspace >"$GIT_DIR"/COMMIT_MSG
|
||||
|
||||
# Test whether the commit message has any content we didn't supply.
|
||||
have_commitmsg=
|
||||
grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
|
||||
git stripspace > "$GIT_DIR"/COMMIT_BAREMSG
|
||||
|
||||
# Is the commit message totally empty?
|
||||
if test -s "$GIT_DIR"/COMMIT_BAREMSG
|
||||
then
|
||||
if test "$templatefile" != ""
|
||||
then
|
||||
# Test whether this is just the unaltered template.
|
||||
if cnt=$(sed -e '/^#/d' < "$templatefile" |
|
||||
git stripspace |
|
||||
diff "$GIT_DIR"/COMMIT_BAREMSG - |
|
||||
wc -l) &&
|
||||
test 0 -lt $cnt
|
||||
then
|
||||
have_commitmsg=t
|
||||
fi
|
||||
else
|
||||
# No template, so the content in the commit message must
|
||||
# have come from the user.
|
||||
have_commitmsg=t
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f "$GIT_DIR"/COMMIT_BAREMSG
|
||||
|
||||
if test "$have_commitmsg" = "t"
|
||||
then
|
||||
if test -z "$TMP_INDEX"
|
||||
then
|
||||
tree=$(GIT_INDEX_FILE="$USE_INDEX" git write-tree)
|
||||
else
|
||||
tree=$(GIT_INDEX_FILE="$TMP_INDEX" git write-tree) &&
|
||||
rm -f "$TMP_INDEX"
|
||||
fi &&
|
||||
commit=$(git commit-tree $tree $PARENTS <"$GIT_DIR/COMMIT_MSG") &&
|
||||
rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
|
||||
git update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" &&
|
||||
rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" &&
|
||||
if test -f "$NEXT_INDEX"
|
||||
then
|
||||
mv "$NEXT_INDEX" "$THIS_INDEX"
|
||||
else
|
||||
: ;# happy
|
||||
fi
|
||||
else
|
||||
echo >&2 "* no commit message? aborting commit."
|
||||
false
|
||||
fi
|
||||
ret="$?"
|
||||
rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
|
||||
|
||||
cd_to_toplevel
|
||||
|
||||
git rerere
|
||||
|
||||
if test "$ret" = 0
|
||||
then
|
||||
git gc --auto
|
||||
if test -x "$GIT_DIR"/hooks/post-commit
|
||||
then
|
||||
"$GIT_DIR"/hooks/post-commit
|
||||
fi
|
||||
if test -z "$quiet"
|
||||
then
|
||||
commit=$(git diff-tree --always --shortstat --pretty="format:%h: %s"\
|
||||
--abbrev --summary --root HEAD --)
|
||||
echo "Created${initial_commit:+ initial} commit $commit"
|
||||
fi
|
||||
fi
|
||||
|
||||
exit "$ret"
|
|
@ -1,481 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
# Copyright (c) 2009, 2010 David Aguilar
|
||||
# Copyright (c) 2012 Tim Henigan
|
||||
#
|
||||
# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
|
||||
# git-difftool--helper script.
|
||||
#
|
||||
# This script exports GIT_EXTERNAL_DIFF and GIT_PAGER for use by git.
|
||||
# The GIT_DIFF* variables are exported for use by git-difftool--helper.
|
||||
#
|
||||
# Any arguments that are unknown to this script are forwarded to 'git diff'.
|
||||
|
||||
use 5.008;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Git::LoadCPAN::Error qw(:try);
|
||||
use File::Basename qw(dirname);
|
||||
use File::Copy;
|
||||
use File::Find;
|
||||
use File::stat;
|
||||
use File::Path qw(mkpath rmtree);
|
||||
use File::Temp qw(tempdir);
|
||||
use Getopt::Long qw(:config pass_through);
|
||||
use Git;
|
||||
use Git::I18N;
|
||||
|
||||
sub usage
|
||||
{
|
||||
my $exitcode = shift;
|
||||
print << 'USAGE';
|
||||
usage: git difftool [-t|--tool=<tool>] [--tool-help]
|
||||
[-x|--extcmd=<cmd>]
|
||||
[-g|--gui] [--no-gui]
|
||||
[--prompt] [-y|--no-prompt]
|
||||
[-d|--dir-diff]
|
||||
['git diff' options]
|
||||
USAGE
|
||||
exit($exitcode);
|
||||
}
|
||||
|
||||
sub print_tool_help
|
||||
{
|
||||
# See the comment at the bottom of file_diff() for the reason behind
|
||||
# using system() followed by exit() instead of exec().
|
||||
my $rc = system(qw(git mergetool --tool-help=diff));
|
||||
exit($rc | ($rc >> 8));
|
||||
}
|
||||
|
||||
sub exit_cleanup
|
||||
{
|
||||
my ($tmpdir, $status) = @_;
|
||||
my $errno = $!;
|
||||
rmtree($tmpdir);
|
||||
if ($status and $errno) {
|
||||
my ($package, $file, $line) = caller();
|
||||
warn "$file line $line: $errno\n";
|
||||
}
|
||||
exit($status | ($status >> 8));
|
||||
}
|
||||
|
||||
sub use_wt_file
|
||||
{
|
||||
my ($file, $sha1) = @_;
|
||||
my $null_sha1 = '0' x 40;
|
||||
|
||||
if (-l $file || ! -e _) {
|
||||
return (0, $null_sha1);
|
||||
}
|
||||
|
||||
my $wt_sha1 = Git::command_oneline('hash-object', $file);
|
||||
my $use = ($sha1 eq $null_sha1) || ($sha1 eq $wt_sha1);
|
||||
return ($use, $wt_sha1);
|
||||
}
|
||||
|
||||
sub changed_files
|
||||
{
|
||||
my ($repo_path, $index, $worktree) = @_;
|
||||
$ENV{GIT_INDEX_FILE} = $index;
|
||||
|
||||
my @gitargs = ('--git-dir', $repo_path, '--work-tree', $worktree);
|
||||
my @refreshargs = (
|
||||
@gitargs, 'update-index',
|
||||
'--really-refresh', '-q', '--unmerged');
|
||||
try {
|
||||
Git::command_oneline(@refreshargs);
|
||||
} catch Git::Error::Command with {};
|
||||
|
||||
my @diffargs = (@gitargs, 'diff-files', '--name-only', '-z');
|
||||
my $line = Git::command_oneline(@diffargs);
|
||||
my @files;
|
||||
if (defined $line) {
|
||||
@files = split('\0', $line);
|
||||
} else {
|
||||
@files = ();
|
||||
}
|
||||
|
||||
delete($ENV{GIT_INDEX_FILE});
|
||||
|
||||
return map { $_ => 1 } @files;
|
||||
}
|
||||
|
||||
sub setup_dir_diff
|
||||
{
|
||||
my ($worktree, $symlinks) = @_;
|
||||
my @gitargs = ('diff', '--raw', '--no-abbrev', '-z', @ARGV);
|
||||
my $diffrtn = Git::command_oneline(@gitargs);
|
||||
exit(0) unless defined($diffrtn);
|
||||
|
||||
# Go to the root of the worktree now that we've captured the list of
|
||||
# changed files. The paths returned by diff --raw are relative to the
|
||||
# top-level of the repository, but we defer changing directories so
|
||||
# that @ARGV can perform pathspec limiting in the current directory.
|
||||
chdir($worktree);
|
||||
|
||||
# Build index info for left and right sides of the diff
|
||||
my $submodule_mode = '160000';
|
||||
my $symlink_mode = '120000';
|
||||
my $null_mode = '0' x 6;
|
||||
my $null_sha1 = '0' x 40;
|
||||
my $lindex = '';
|
||||
my $rindex = '';
|
||||
my $wtindex = '';
|
||||
my %submodule;
|
||||
my %symlink;
|
||||
my @files = ();
|
||||
my %working_tree_dups = ();
|
||||
my @rawdiff = split('\0', $diffrtn);
|
||||
|
||||
my $i = 0;
|
||||
while ($i < $#rawdiff) {
|
||||
if ($rawdiff[$i] =~ /^::/) {
|
||||
warn __ <<'EOF';
|
||||
Combined diff formats ('-c' and '--cc') are not supported in
|
||||
directory diff mode ('-d' and '--dir-diff').
|
||||
EOF
|
||||
exit(1);
|
||||
}
|
||||
|
||||
my ($lmode, $rmode, $lsha1, $rsha1, $status) =
|
||||
split(' ', substr($rawdiff[$i], 1));
|
||||
my $src_path = $rawdiff[$i + 1];
|
||||
my $dst_path;
|
||||
|
||||
if ($status =~ /^[CR]/) {
|
||||
$dst_path = $rawdiff[$i + 2];
|
||||
$i += 3;
|
||||
} else {
|
||||
$dst_path = $src_path;
|
||||
$i += 2;
|
||||
}
|
||||
|
||||
if ($lmode eq $submodule_mode or $rmode eq $submodule_mode) {
|
||||
$submodule{$src_path}{left} = $lsha1;
|
||||
if ($lsha1 ne $rsha1) {
|
||||
$submodule{$dst_path}{right} = $rsha1;
|
||||
} else {
|
||||
$submodule{$dst_path}{right} = "$rsha1-dirty";
|
||||
}
|
||||
next;
|
||||
}
|
||||
|
||||
if ($lmode eq $symlink_mode) {
|
||||
$symlink{$src_path}{left} =
|
||||
Git::command_oneline('show', $lsha1);
|
||||
}
|
||||
|
||||
if ($rmode eq $symlink_mode) {
|
||||
$symlink{$dst_path}{right} =
|
||||
Git::command_oneline('show', $rsha1);
|
||||
}
|
||||
|
||||
if ($lmode ne $null_mode and $status !~ /^C/) {
|
||||
$lindex .= "$lmode $lsha1\t$src_path\0";
|
||||
}
|
||||
|
||||
if ($rmode ne $null_mode) {
|
||||
# Avoid duplicate entries
|
||||
if ($working_tree_dups{$dst_path}++) {
|
||||
next;
|
||||
}
|
||||
my ($use, $wt_sha1) =
|
||||
use_wt_file($dst_path, $rsha1);
|
||||
if ($use) {
|
||||
push @files, $dst_path;
|
||||
$wtindex .= "$rmode $wt_sha1\t$dst_path\0";
|
||||
} else {
|
||||
$rindex .= "$rmode $rsha1\t$dst_path\0";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Go to the root of the worktree so that the left index files
|
||||
# are properly setup -- the index is toplevel-relative.
|
||||
chdir($worktree);
|
||||
|
||||
# Setup temp directories
|
||||
my $tmpdir = tempdir('git-difftool.XXXXX', CLEANUP => 0, TMPDIR => 1);
|
||||
my $ldir = "$tmpdir/left";
|
||||
my $rdir = "$tmpdir/right";
|
||||
mkpath($ldir) or exit_cleanup($tmpdir, 1);
|
||||
mkpath($rdir) or exit_cleanup($tmpdir, 1);
|
||||
|
||||
# Populate the left and right directories based on each index file
|
||||
my ($inpipe, $ctx);
|
||||
$ENV{GIT_INDEX_FILE} = "$tmpdir/lindex";
|
||||
($inpipe, $ctx) =
|
||||
Git::command_input_pipe('update-index', '-z', '--index-info');
|
||||
print($inpipe $lindex);
|
||||
Git::command_close_pipe($inpipe, $ctx);
|
||||
|
||||
my $rc = system('git', 'checkout-index', '--all', "--prefix=$ldir/");
|
||||
exit_cleanup($tmpdir, $rc) if $rc != 0;
|
||||
|
||||
$ENV{GIT_INDEX_FILE} = "$tmpdir/rindex";
|
||||
($inpipe, $ctx) =
|
||||
Git::command_input_pipe('update-index', '-z', '--index-info');
|
||||
print($inpipe $rindex);
|
||||
Git::command_close_pipe($inpipe, $ctx);
|
||||
|
||||
$rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/");
|
||||
exit_cleanup($tmpdir, $rc) if $rc != 0;
|
||||
|
||||
$ENV{GIT_INDEX_FILE} = "$tmpdir/wtindex";
|
||||
($inpipe, $ctx) =
|
||||
Git::command_input_pipe('update-index', '--info-only', '-z', '--index-info');
|
||||
print($inpipe $wtindex);
|
||||
Git::command_close_pipe($inpipe, $ctx);
|
||||
|
||||
# If $GIT_DIR was explicitly set just for the update/checkout
|
||||
# commands, then it should be unset before continuing.
|
||||
delete($ENV{GIT_INDEX_FILE});
|
||||
|
||||
# Changes in the working tree need special treatment since they are
|
||||
# not part of the index.
|
||||
for my $file (@files) {
|
||||
my $dir = dirname($file);
|
||||
unless (-d "$rdir/$dir") {
|
||||
mkpath("$rdir/$dir") or
|
||||
exit_cleanup($tmpdir, 1);
|
||||
}
|
||||
if ($symlinks) {
|
||||
symlink("$worktree/$file", "$rdir/$file") or
|
||||
exit_cleanup($tmpdir, 1);
|
||||
} else {
|
||||
copy($file, "$rdir/$file") or
|
||||
exit_cleanup($tmpdir, 1);
|
||||
|
||||
my $mode = stat($file)->mode;
|
||||
chmod($mode, "$rdir/$file") or
|
||||
exit_cleanup($tmpdir, 1);
|
||||
}
|
||||
}
|
||||
|
||||
# Changes to submodules require special treatment. This loop writes a
|
||||
# temporary file to both the left and right directories to show the
|
||||
# change in the recorded SHA1 for the submodule.
|
||||
for my $path (keys %submodule) {
|
||||
my $ok = 0;
|
||||
if (defined($submodule{$path}{left})) {
|
||||
$ok = write_to_file("$ldir/$path",
|
||||
"Subproject commit $submodule{$path}{left}");
|
||||
}
|
||||
if (defined($submodule{$path}{right})) {
|
||||
$ok = write_to_file("$rdir/$path",
|
||||
"Subproject commit $submodule{$path}{right}");
|
||||
}
|
||||
exit_cleanup($tmpdir, 1) if not $ok;
|
||||
}
|
||||
|
||||
# Symbolic links require special treatment. The standard "git diff"
|
||||
# shows only the link itself, not the contents of the link target.
|
||||
# This loop replicates that behavior.
|
||||
for my $path (keys %symlink) {
|
||||
my $ok = 0;
|
||||
if (defined($symlink{$path}{left})) {
|
||||
$ok = write_to_file("$ldir/$path",
|
||||
$symlink{$path}{left});
|
||||
}
|
||||
if (defined($symlink{$path}{right})) {
|
||||
$ok = write_to_file("$rdir/$path",
|
||||
$symlink{$path}{right});
|
||||
}
|
||||
exit_cleanup($tmpdir, 1) if not $ok;
|
||||
}
|
||||
|
||||
return ($ldir, $rdir, $tmpdir, @files);
|
||||
}
|
||||
|
||||
sub write_to_file
|
||||
{
|
||||
my $path = shift;
|
||||
my $value = shift;
|
||||
|
||||
# Make sure the path to the file exists
|
||||
my $dir = dirname($path);
|
||||
unless (-d "$dir") {
|
||||
mkpath("$dir") or return 0;
|
||||
}
|
||||
|
||||
# If the file already exists in that location, delete it. This
|
||||
# is required in the case of symbolic links.
|
||||
unlink($path);
|
||||
|
||||
open(my $fh, '>', $path) or return 0;
|
||||
print($fh $value);
|
||||
close($fh);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub main
|
||||
{
|
||||
# parse command-line options. all unrecognized options and arguments
|
||||
# are passed through to the 'git diff' command.
|
||||
my %opts = (
|
||||
difftool_cmd => undef,
|
||||
dirdiff => undef,
|
||||
extcmd => undef,
|
||||
gui => undef,
|
||||
help => undef,
|
||||
prompt => undef,
|
||||
symlinks => $^O ne 'cygwin' &&
|
||||
$^O ne 'MSWin32' && $^O ne 'msys',
|
||||
tool_help => undef,
|
||||
trust_exit_code => undef,
|
||||
);
|
||||
GetOptions('g|gui!' => \$opts{gui},
|
||||
'd|dir-diff' => \$opts{dirdiff},
|
||||
'h' => \$opts{help},
|
||||
'prompt!' => \$opts{prompt},
|
||||
'y' => sub { $opts{prompt} = 0; },
|
||||
'symlinks' => \$opts{symlinks},
|
||||
'no-symlinks' => sub { $opts{symlinks} = 0; },
|
||||
't|tool:s' => \$opts{difftool_cmd},
|
||||
'tool-help' => \$opts{tool_help},
|
||||
'trust-exit-code' => \$opts{trust_exit_code},
|
||||
'no-trust-exit-code' => sub { $opts{trust_exit_code} = 0; },
|
||||
'x|extcmd:s' => \$opts{extcmd});
|
||||
|
||||
if (defined($opts{help})) {
|
||||
usage(0);
|
||||
}
|
||||
if (defined($opts{tool_help})) {
|
||||
print_tool_help();
|
||||
}
|
||||
if (defined($opts{difftool_cmd})) {
|
||||
if (length($opts{difftool_cmd}) > 0) {
|
||||
$ENV{GIT_DIFF_TOOL} = $opts{difftool_cmd};
|
||||
} else {
|
||||
print __("No <tool> given for --tool=<tool>\n");
|
||||
usage(1);
|
||||
}
|
||||
}
|
||||
if (defined($opts{extcmd})) {
|
||||
if (length($opts{extcmd}) > 0) {
|
||||
$ENV{GIT_DIFFTOOL_EXTCMD} = $opts{extcmd};
|
||||
} else {
|
||||
print __("No <cmd> given for --extcmd=<cmd>\n");
|
||||
usage(1);
|
||||
}
|
||||
}
|
||||
if ($opts{gui}) {
|
||||
my $guitool = Git::config('diff.guitool');
|
||||
if (defined($guitool) && length($guitool) > 0) {
|
||||
$ENV{GIT_DIFF_TOOL} = $guitool;
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined $opts{trust_exit_code}) {
|
||||
$opts{trust_exit_code} = Git::config_bool('difftool.trustExitCode');
|
||||
}
|
||||
if ($opts{trust_exit_code}) {
|
||||
$ENV{GIT_DIFFTOOL_TRUST_EXIT_CODE} = 'true';
|
||||
} else {
|
||||
$ENV{GIT_DIFFTOOL_TRUST_EXIT_CODE} = 'false';
|
||||
}
|
||||
|
||||
# In directory diff mode, 'git-difftool--helper' is called once
|
||||
# to compare the a/b directories. In file diff mode, 'git diff'
|
||||
# will invoke a separate instance of 'git-difftool--helper' for
|
||||
# each file that changed.
|
||||
if (defined($opts{dirdiff})) {
|
||||
dir_diff($opts{extcmd}, $opts{symlinks});
|
||||
} else {
|
||||
file_diff($opts{prompt});
|
||||
}
|
||||
}
|
||||
|
||||
sub dir_diff
|
||||
{
|
||||
my ($extcmd, $symlinks) = @_;
|
||||
my $rc;
|
||||
my $error = 0;
|
||||
my $repo = Git->repository();
|
||||
my $repo_path = $repo->repo_path();
|
||||
my $worktree = $repo->wc_path();
|
||||
$worktree =~ s|/$||; # Avoid double slashes in symlink targets
|
||||
my ($a, $b, $tmpdir, @files) = setup_dir_diff($worktree, $symlinks);
|
||||
|
||||
if (defined($extcmd)) {
|
||||
$rc = system($extcmd, $a, $b);
|
||||
} else {
|
||||
$ENV{GIT_DIFFTOOL_DIRDIFF} = 'true';
|
||||
$rc = system('git', 'difftool--helper', $a, $b);
|
||||
}
|
||||
# If the diff including working copy files and those
|
||||
# files were modified during the diff, then the changes
|
||||
# should be copied back to the working tree.
|
||||
# Do not copy back files when symlinks are used and the
|
||||
# external tool did not replace the original link with a file.
|
||||
#
|
||||
# These hashes are loaded lazily since they aren't needed
|
||||
# in the common case of --symlinks and the difftool updating
|
||||
# files through the symlink.
|
||||
my %wt_modified;
|
||||
my %tmp_modified;
|
||||
my $indices_loaded = 0;
|
||||
|
||||
for my $file (@files) {
|
||||
next if $symlinks && -l "$b/$file";
|
||||
next if ! -f "$b/$file";
|
||||
|
||||
if (!$indices_loaded) {
|
||||
%wt_modified = changed_files(
|
||||
$repo_path, "$tmpdir/wtindex", $worktree);
|
||||
%tmp_modified = changed_files(
|
||||
$repo_path, "$tmpdir/wtindex", $b);
|
||||
$indices_loaded = 1;
|
||||
}
|
||||
|
||||
if (exists $wt_modified{$file} and exists $tmp_modified{$file}) {
|
||||
warn sprintf(__(
|
||||
"warning: Both files modified:\n" .
|
||||
"'%s/%s' and '%s/%s'.\n" .
|
||||
"warning: Working tree file has been left.\n" .
|
||||
"warning:\n"), $worktree, $file, $b, $file);
|
||||
$error = 1;
|
||||
} elsif (exists $tmp_modified{$file}) {
|
||||
my $mode = stat("$b/$file")->mode;
|
||||
copy("$b/$file", $file) or
|
||||
exit_cleanup($tmpdir, 1);
|
||||
|
||||
chmod($mode, $file) or
|
||||
exit_cleanup($tmpdir, 1);
|
||||
}
|
||||
}
|
||||
if ($error) {
|
||||
warn sprintf(__(
|
||||
"warning: Temporary files exist in '%s'.\n" .
|
||||
"warning: You may want to cleanup or recover these.\n"), $tmpdir);
|
||||
exit(1);
|
||||
} else {
|
||||
exit_cleanup($tmpdir, $rc);
|
||||
}
|
||||
}
|
||||
|
||||
sub file_diff
|
||||
{
|
||||
my ($prompt) = @_;
|
||||
|
||||
if (defined($prompt)) {
|
||||
if ($prompt) {
|
||||
$ENV{GIT_DIFFTOOL_PROMPT} = 'true';
|
||||
} else {
|
||||
$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
|
||||
}
|
||||
}
|
||||
|
||||
$ENV{GIT_PAGER} = '';
|
||||
$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
|
||||
|
||||
# ActiveState Perl for Win32 does not implement POSIX semantics of
|
||||
# exec* system call. It just spawns the given executable and finishes
|
||||
# the starting program, exiting with code 0.
|
||||
# system will at least catch the errors returned by git diff,
|
||||
# allowing the caller of git difftool better handling of failures.
|
||||
my $rc = system('git', 'diff', @ARGV);
|
||||
exit($rc | ($rc >> 8));
|
||||
}
|
||||
|
||||
main();
|
|
@ -1,379 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
|
||||
USAGE='<fetch-options> <repository> <refspec>...'
|
||||
SUBDIRECTORY_OK=Yes
|
||||
. git-sh-setup
|
||||
set_reflog_action "fetch $*"
|
||||
cd_to_toplevel ;# probably unnecessary...
|
||||
|
||||
. git-parse-remote
|
||||
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
|
||||
_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
|
||||
|
||||
LF='
|
||||
'
|
||||
IFS="$LF"
|
||||
|
||||
no_tags=
|
||||
tags=
|
||||
append=
|
||||
force=
|
||||
verbose=
|
||||
update_head_ok=
|
||||
exec=
|
||||
keep=
|
||||
shallow_depth=
|
||||
no_progress=
|
||||
test -t 1 || no_progress=--no-progress
|
||||
quiet=
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-a|--a|--ap|--app|--appe|--appen|--append)
|
||||
append=t
|
||||
;;
|
||||
--upl|--uplo|--uploa|--upload|--upload-|--upload-p|\
|
||||
--upload-pa|--upload-pac|--upload-pack)
|
||||
shift
|
||||
exec="--upload-pack=$1"
|
||||
;;
|
||||
--upl=*|--uplo=*|--uploa=*|--upload=*|\
|
||||
--upload-=*|--upload-p=*|--upload-pa=*|--upload-pac=*|--upload-pack=*)
|
||||
exec=--upload-pack=$(expr "z$1" : 'z-[^=]*=\(.*\)')
|
||||
shift
|
||||
;;
|
||||
-f|--f|--fo|--for|--forc|--force)
|
||||
force=t
|
||||
;;
|
||||
-t|--t|--ta|--tag|--tags)
|
||||
tags=t
|
||||
;;
|
||||
-n|--n|--no|--no-|--no-t|--no-ta|--no-tag|--no-tags)
|
||||
no_tags=t
|
||||
;;
|
||||
-u|--u|--up|--upd|--upda|--updat|--update|--update-|--update-h|\
|
||||
--update-he|--update-hea|--update-head|--update-head-|\
|
||||
--update-head-o|--update-head-ok)
|
||||
update_head_ok=t
|
||||
;;
|
||||
-q|--q|--qu|--qui|--quie|--quiet)
|
||||
quiet=--quiet
|
||||
;;
|
||||
-v|--verbose)
|
||||
verbose="$verbose"Yes
|
||||
;;
|
||||
-k|--k|--ke|--kee|--keep)
|
||||
keep='-k -k'
|
||||
;;
|
||||
--depth=*)
|
||||
shallow_depth="--depth=$(expr "z$1" : 'z-[^=]*=\(.*\)')"
|
||||
;;
|
||||
--depth)
|
||||
shift
|
||||
shallow_depth="--depth=$1"
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
case "$#" in
|
||||
0)
|
||||
origin=$(get_default_remote)
|
||||
test -n "$(get_remote_url ${origin})" ||
|
||||
die "Where do you want to fetch from today?"
|
||||
set x $origin ; shift ;;
|
||||
esac
|
||||
|
||||
if test -z "$exec"
|
||||
then
|
||||
# No command line override and we have configuration for the remote.
|
||||
exec="--upload-pack=$(get_uploadpack $1)"
|
||||
fi
|
||||
|
||||
remote_nick="$1"
|
||||
remote=$(get_remote_url "$@")
|
||||
refs=
|
||||
rref=
|
||||
rsync_slurped_objects=
|
||||
|
||||
if test "" = "$append"
|
||||
then
|
||||
: >"$GIT_DIR/FETCH_HEAD"
|
||||
fi
|
||||
|
||||
# Global that is reused later
|
||||
ls_remote_result=$(git ls-remote $exec "$remote") ||
|
||||
die "Cannot get the repository state from $remote"
|
||||
|
||||
append_fetch_head () {
|
||||
flags=
|
||||
test -n "$verbose" && flags="$flags$LF-v"
|
||||
test -n "$force$single_force" && flags="$flags$LF-f"
|
||||
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \
|
||||
git fetch--tool $flags append-fetch-head "$@"
|
||||
}
|
||||
|
||||
# updating the current HEAD with git-fetch in a bare
|
||||
# repository is always fine.
|
||||
if test -z "$update_head_ok" && test $(is_bare_repository) = false
|
||||
then
|
||||
orig_head=$(git rev-parse --verify HEAD 2>/dev/null)
|
||||
fi
|
||||
|
||||
# Allow --tags/--notags from remote.$1.tagopt
|
||||
case "$tags$no_tags" in
|
||||
'')
|
||||
case "$(git config --get "remote.$1.tagopt")" in
|
||||
--tags)
|
||||
tags=t ;;
|
||||
--no-tags)
|
||||
no_tags=t ;;
|
||||
esac
|
||||
esac
|
||||
|
||||
# If --tags (and later --heads or --all) is specified, then we are
|
||||
# not talking about defaults stored in Pull: line of remotes or
|
||||
# branches file, and just fetch those and refspecs explicitly given.
|
||||
# Otherwise we do what we always did.
|
||||
|
||||
reflist=$(get_remote_refs_for_fetch "$@")
|
||||
if test "$tags"
|
||||
then
|
||||
taglist=$(IFS=' ' &&
|
||||
echo "$ls_remote_result" |
|
||||
git show-ref --exclude-existing=refs/tags/ |
|
||||
while read sha1 name
|
||||
do
|
||||
echo ".${name}:${name}"
|
||||
done) || exit
|
||||
if test "$#" -gt 1
|
||||
then
|
||||
# remote URL plus explicit refspecs; we need to merge them.
|
||||
reflist="$reflist$LF$taglist"
|
||||
else
|
||||
# No explicit refspecs; fetch tags only.
|
||||
reflist=$taglist
|
||||
fi
|
||||
fi
|
||||
|
||||
fetch_all_at_once () {
|
||||
|
||||
eval=$(echo "$1" | git fetch--tool parse-reflist "-")
|
||||
eval "$eval"
|
||||
|
||||
( : subshell because we muck with IFS
|
||||
IFS=" $LF"
|
||||
(
|
||||
if test "$remote" = . ; then
|
||||
git show-ref $rref || echo failed "$remote"
|
||||
elif test -f "$remote" ; then
|
||||
test -n "$shallow_depth" &&
|
||||
die "shallow clone with bundle is not supported"
|
||||
git bundle unbundle "$remote" $rref ||
|
||||
echo failed "$remote"
|
||||
else
|
||||
if test -d "$remote" &&
|
||||
|
||||
# The remote might be our alternate. With
|
||||
# this optimization we will bypass fetch-pack
|
||||
# altogether, which means we cannot be doing
|
||||
# the shallow stuff at all.
|
||||
test ! -f "$GIT_DIR/shallow" &&
|
||||
test -z "$shallow_depth" &&
|
||||
|
||||
# See if all of what we are going to fetch are
|
||||
# connected to our repository's tips, in which
|
||||
# case we do not have to do any fetch.
|
||||
theirs=$(echo "$ls_remote_result" | \
|
||||
git fetch--tool -s pick-rref "$rref" "-") &&
|
||||
|
||||
# This will barf when $theirs reach an object that
|
||||
# we do not have in our repository. Otherwise,
|
||||
# we already have everything the fetch would bring in.
|
||||
git rev-list --objects $theirs --not --all \
|
||||
>/dev/null 2>/dev/null
|
||||
then
|
||||
echo "$ls_remote_result" | \
|
||||
git fetch--tool pick-rref "$rref" "-"
|
||||
else
|
||||
flags=
|
||||
case $verbose in
|
||||
YesYes*)
|
||||
flags="-v"
|
||||
;;
|
||||
esac
|
||||
git-fetch-pack --thin $exec $keep $shallow_depth \
|
||||
$quiet $no_progress $flags "$remote" $rref ||
|
||||
echo failed "$remote"
|
||||
fi
|
||||
fi
|
||||
) |
|
||||
(
|
||||
flags=
|
||||
test -n "$verbose" && flags="$flags -v"
|
||||
test -n "$force" && flags="$flags -f"
|
||||
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \
|
||||
git fetch--tool $flags native-store \
|
||||
"$remote" "$remote_nick" "$refs"
|
||||
)
|
||||
) || exit
|
||||
|
||||
}
|
||||
|
||||
fetch_per_ref () {
|
||||
reflist="$1"
|
||||
refs=
|
||||
rref=
|
||||
|
||||
for ref in $reflist
|
||||
do
|
||||
refs="$refs$LF$ref"
|
||||
|
||||
# These are relative path from $GIT_DIR, typically starting at refs/
|
||||
# but may be HEAD
|
||||
if expr "z$ref" : 'z\.' >/dev/null
|
||||
then
|
||||
not_for_merge=t
|
||||
ref=$(expr "z$ref" : 'z\.\(.*\)')
|
||||
else
|
||||
not_for_merge=
|
||||
fi
|
||||
if expr "z$ref" : 'z+' >/dev/null
|
||||
then
|
||||
single_force=t
|
||||
ref=$(expr "z$ref" : 'z+\(.*\)')
|
||||
else
|
||||
single_force=
|
||||
fi
|
||||
remote_name=$(expr "z$ref" : 'z\([^:]*\):')
|
||||
local_name=$(expr "z$ref" : 'z[^:]*:\(.*\)')
|
||||
|
||||
rref="$rref$LF$remote_name"
|
||||
|
||||
# There are transports that can fetch only one head at a time...
|
||||
case "$remote" in
|
||||
http://* | https://* | ftp://*)
|
||||
test -n "$shallow_depth" &&
|
||||
die "shallow clone with http not supported"
|
||||
proto=$(expr "$remote" : '\([^:]*\):')
|
||||
if [ -n "$GIT_SSL_NO_VERIFY" ]; then
|
||||
curl_extra_args="-k"
|
||||
fi
|
||||
if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
|
||||
"$(git config --bool http.noEPSV)" = true ]; then
|
||||
noepsv_opt="--disable-epsv"
|
||||
fi
|
||||
|
||||
# Find $remote_name from ls-remote output.
|
||||
head=$(echo "$ls_remote_result" | \
|
||||
git fetch--tool -s pick-rref "$remote_name" "-")
|
||||
expr "z$head" : "z$_x40\$" >/dev/null ||
|
||||
die "No such ref $remote_name at $remote"
|
||||
echo >&2 "Fetching $remote_name from $remote using $proto"
|
||||
case "$quiet" in '') v=-v ;; *) v= ;; esac
|
||||
git-http-fetch $v -a "$head" "$remote" || exit
|
||||
;;
|
||||
rsync://*)
|
||||
test -n "$shallow_depth" &&
|
||||
die "shallow clone with rsync not supported"
|
||||
TMP_HEAD="$GIT_DIR/TMP_HEAD"
|
||||
rsync -L -q "$remote/$remote_name" "$TMP_HEAD" || exit 1
|
||||
head=$(git rev-parse --verify TMP_HEAD)
|
||||
rm -f "$TMP_HEAD"
|
||||
case "$quiet" in '') v=-v ;; *) v= ;; esac
|
||||
test "$rsync_slurped_objects" || {
|
||||
rsync -a $v --ignore-existing --exclude info \
|
||||
"$remote/objects/" "$GIT_OBJECT_DIRECTORY/" || exit
|
||||
|
||||
# Look at objects/info/alternates for rsync -- http will
|
||||
# support it natively and git native ones will do it on
|
||||
# the remote end. Not having that file is not a crime.
|
||||
rsync -q "$remote/objects/info/alternates" \
|
||||
"$GIT_DIR/TMP_ALT" 2>/dev/null ||
|
||||
rm -f "$GIT_DIR/TMP_ALT"
|
||||
if test -f "$GIT_DIR/TMP_ALT"
|
||||
then
|
||||
resolve_alternates "$remote" <"$GIT_DIR/TMP_ALT" |
|
||||
while read alt
|
||||
do
|
||||
case "$alt" in 'bad alternate: '*) die "$alt";; esac
|
||||
echo >&2 "Getting alternate: $alt"
|
||||
rsync -av --ignore-existing --exclude info \
|
||||
"$alt" "$GIT_OBJECT_DIRECTORY/" || exit
|
||||
done
|
||||
rm -f "$GIT_DIR/TMP_ALT"
|
||||
fi
|
||||
rsync_slurped_objects=t
|
||||
}
|
||||
;;
|
||||
esac
|
||||
|
||||
append_fetch_head "$head" "$remote" \
|
||||
"$remote_name" "$remote_nick" "$local_name" "$not_for_merge" || exit
|
||||
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
fetch_main () {
|
||||
case "$remote" in
|
||||
http://* | https://* | ftp://* | rsync://* )
|
||||
fetch_per_ref "$@"
|
||||
;;
|
||||
*)
|
||||
fetch_all_at_once "$@"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
fetch_main "$reflist" || exit
|
||||
|
||||
# automated tag following
|
||||
case "$no_tags$tags" in
|
||||
'')
|
||||
case "$reflist" in
|
||||
*:refs/*)
|
||||
# effective only when we are following remote branch
|
||||
# using local tracking branch.
|
||||
taglist=$(IFS=' ' &&
|
||||
echo "$ls_remote_result" |
|
||||
git show-ref --exclude-existing=refs/tags/ |
|
||||
while read sha1 name
|
||||
do
|
||||
git cat-file -t "$sha1" >/dev/null 2>&1 || continue
|
||||
echo >&2 "Auto-following $name"
|
||||
echo ".${name}:${name}"
|
||||
done)
|
||||
esac
|
||||
case "$taglist" in
|
||||
'') ;;
|
||||
?*)
|
||||
# do not deepen a shallow tree when following tags
|
||||
shallow_depth=
|
||||
fetch_main "$taglist" || exit ;;
|
||||
esac
|
||||
esac
|
||||
|
||||
# If the original head was empty (i.e. no "master" yet), or
|
||||
# if we were told not to worry, we do not have to check.
|
||||
case "$orig_head" in
|
||||
'')
|
||||
;;
|
||||
?*)
|
||||
curr_head=$(git rev-parse --verify HEAD 2>/dev/null)
|
||||
if test "$curr_head" != "$orig_head"
|
||||
then
|
||||
git update-ref \
|
||||
-m "$GIT_REFLOG_ACTION: Undoing incorrectly fetched HEAD." \
|
||||
HEAD "$orig_head"
|
||||
die "Cannot fetch into the current branch."
|
||||
fi
|
||||
;;
|
||||
esac
|
|
@ -1,37 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2006, Shawn O. Pearce
|
||||
#
|
||||
# Cleanup unreachable files and optimize the repository.
|
||||
|
||||
USAGE='[--prune]'
|
||||
SUBDIRECTORY_OK=Yes
|
||||
. git-sh-setup
|
||||
|
||||
no_prune=:
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
--prune)
|
||||
no_prune=
|
||||
;;
|
||||
--)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
case "$(git config --get gc.packrefs)" in
|
||||
notbare|"")
|
||||
test $(is_bare_repository) = true || pack_refs=true;;
|
||||
*)
|
||||
pack_refs=$(git config --bool --get gc.packrefs)
|
||||
esac
|
||||
|
||||
test "true" != "$pack_refs" ||
|
||||
git pack-refs --prune &&
|
||||
git reflog expire --all &&
|
||||
git-repack -a -d -l &&
|
||||
$no_prune git prune &&
|
||||
git rerere gc || exit
|
|
@ -1,15 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Linus Torvalds
|
||||
#
|
||||
|
||||
USAGE='[--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [git-rev-list options]'
|
||||
SUBDIRECTORY_OK='Yes'
|
||||
. git-sh-setup
|
||||
|
||||
revs=$(git-rev-parse --revs-only --no-flags --default HEAD "$@") || exit
|
||||
[ "$revs" ] || {
|
||||
die "No HEAD ref"
|
||||
}
|
||||
git-rev-list --pretty $(git-rev-parse --default HEAD "$@") |
|
||||
LESS=-S ${PAGER:-less}
|
|
@ -1,142 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
|
||||
usage () {
|
||||
echo >&2 "usage: $0 [--heads] [--tags] [-u|--upload-pack <upload-pack>]"
|
||||
echo >&2 " <repository> <refs>..."
|
||||
exit 1;
|
||||
}
|
||||
|
||||
die () {
|
||||
echo >&2 "$*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
exec=
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-h|--h|--he|--hea|--head|--heads)
|
||||
heads=heads; shift ;;
|
||||
-t|--t|--ta|--tag|--tags)
|
||||
tags=tags; shift ;;
|
||||
-u|--u|--up|--upl|--uploa|--upload|--upload-|--upload-p|--upload-pa|\
|
||||
--upload-pac|--upload-pack)
|
||||
shift
|
||||
exec="--upload-pack=$1"
|
||||
shift;;
|
||||
-u=*|--u=*|--up=*|--upl=*|--uplo=*|--uploa=*|--upload=*|\
|
||||
--upload-=*|--upload-p=*|--upload-pa=*|--upload-pac=*|--upload-pack=*)
|
||||
exec=--upload-pack=$(expr "z$1" : 'z-[^=]*=\(.*\)')
|
||||
shift;;
|
||||
--)
|
||||
shift; break ;;
|
||||
-*)
|
||||
usage ;;
|
||||
*)
|
||||
break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$#" in 0) usage ;; esac
|
||||
|
||||
case ",$heads,$tags," in
|
||||
,,,) heads=heads tags=tags other=other ;;
|
||||
esac
|
||||
|
||||
. git-parse-remote
|
||||
peek_repo="$(get_remote_url "$@")"
|
||||
shift
|
||||
|
||||
tmp=.ls-remote-$$
|
||||
trap "rm -fr $tmp-*" 0 1 2 3 15
|
||||
tmpdir=$tmp-d
|
||||
|
||||
case "$peek_repo" in
|
||||
http://* | https://* | ftp://* )
|
||||
if [ -n "$GIT_SSL_NO_VERIFY" -o \
|
||||
"$(git config --bool http.sslVerify)" = false ]; then
|
||||
curl_extra_args="-k"
|
||||
fi
|
||||
if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \
|
||||
"$(git config --bool http.noEPSV)" = true ]; then
|
||||
curl_extra_args="${curl_extra_args} --disable-epsv"
|
||||
fi
|
||||
curl -nsf $curl_extra_args --header "Pragma: no-cache" "$peek_repo/info/refs" ||
|
||||
echo "failed slurping"
|
||||
;;
|
||||
|
||||
rsync://* )
|
||||
mkdir $tmpdir &&
|
||||
rsync -rlq "$peek_repo/HEAD" $tmpdir &&
|
||||
rsync -rq "$peek_repo/refs" $tmpdir || {
|
||||
echo "failed slurping"
|
||||
exit
|
||||
}
|
||||
head=$(cat "$tmpdir/HEAD") &&
|
||||
case "$head" in
|
||||
ref:' '*)
|
||||
head=$(expr "z$head" : 'zref: \(.*\)') &&
|
||||
head=$(cat "$tmpdir/$head") || exit
|
||||
esac &&
|
||||
echo "$head HEAD"
|
||||
(cd $tmpdir && find refs -type f) |
|
||||
while read path
|
||||
do
|
||||
tr -d '\012' <"$tmpdir/$path"
|
||||
echo " $path"
|
||||
done &&
|
||||
rm -fr $tmpdir
|
||||
;;
|
||||
|
||||
* )
|
||||
if test -f "$peek_repo" ; then
|
||||
git bundle list-heads "$peek_repo" ||
|
||||
echo "failed slurping"
|
||||
else
|
||||
git-peek-remote $exec "$peek_repo" ||
|
||||
echo "failed slurping"
|
||||
fi
|
||||
;;
|
||||
esac |
|
||||
sort -t ' ' -k 2 |
|
||||
while read sha1 path
|
||||
do
|
||||
case "$sha1" in
|
||||
failed)
|
||||
exit 1 ;;
|
||||
esac
|
||||
case "$path" in
|
||||
refs/heads/*)
|
||||
group=heads ;;
|
||||
refs/tags/*)
|
||||
group=tags ;;
|
||||
*)
|
||||
group=other ;;
|
||||
esac
|
||||
case ",$heads,$tags,$other," in
|
||||
*,$group,*)
|
||||
;;
|
||||
*)
|
||||
continue;;
|
||||
esac
|
||||
case "$#" in
|
||||
0)
|
||||
match=yes ;;
|
||||
*)
|
||||
match=no
|
||||
for pat
|
||||
do
|
||||
case "/$path" in
|
||||
*/$pat )
|
||||
match=yes
|
||||
break ;;
|
||||
esac
|
||||
done
|
||||
esac
|
||||
case "$match" in
|
||||
no)
|
||||
continue ;;
|
||||
esac
|
||||
echo "$sha1 $path"
|
||||
done
|
|
@ -1,14 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Junio C Hamano
|
||||
#
|
||||
# Pretend we resolved the heads, but declare our tree trumps everybody else.
|
||||
#
|
||||
|
||||
# We need to exit with 2 if the index does not match our HEAD tree,
|
||||
# because the current index is what we will be committing as the
|
||||
# merge result.
|
||||
|
||||
git diff-index --quiet --cached HEAD -- || exit 2
|
||||
|
||||
exit 0
|
|
@ -1,620 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Junio C Hamano
|
||||
#
|
||||
|
||||
OPTIONS_KEEPDASHDASH=
|
||||
OPTIONS_SPEC="\
|
||||
git merge [options] <remote>...
|
||||
git merge [options] <msg> HEAD <remote>
|
||||
--
|
||||
stat show a diffstat at the end of the merge
|
||||
n don't show a diffstat at the end of the merge
|
||||
summary (synonym to --stat)
|
||||
log add list of one-line log to merge commit message
|
||||
squash create a single commit instead of doing a merge
|
||||
commit perform a commit if the merge succeeds (default)
|
||||
ff allow fast-forward (default)
|
||||
ff-only abort if fast-forward is not possible
|
||||
rerere-autoupdate update index with any reused conflict resolution
|
||||
s,strategy= merge strategy to use
|
||||
X= option for selected merge strategy
|
||||
m,message= message to be used for the merge commit (if any)
|
||||
"
|
||||
|
||||
SUBDIRECTORY_OK=Yes
|
||||
. git-sh-setup
|
||||
require_work_tree
|
||||
cd_to_toplevel
|
||||
|
||||
test -z "$(git ls-files -u)" ||
|
||||
die "Merge is not possible because you have unmerged files."
|
||||
|
||||
! test -e "$GIT_DIR/MERGE_HEAD" ||
|
||||
die 'You have not concluded your merge (MERGE_HEAD exists).'
|
||||
|
||||
LF='
|
||||
'
|
||||
|
||||
all_strategies='recur recursive octopus resolve stupid ours subtree'
|
||||
all_strategies="$all_strategies recursive-ours recursive-theirs"
|
||||
not_strategies='base file index tree'
|
||||
default_twohead_strategies='recursive'
|
||||
default_octopus_strategies='octopus'
|
||||
no_fast_forward_strategies='subtree ours'
|
||||
no_trivial_strategies='recursive recur subtree ours recursive-ours recursive-theirs'
|
||||
use_strategies=
|
||||
xopt=
|
||||
|
||||
allow_fast_forward=t
|
||||
fast_forward_only=
|
||||
allow_trivial_merge=t
|
||||
squash= no_commit= log_arg= rr_arg=
|
||||
|
||||
dropsave() {
|
||||
rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
|
||||
"$GIT_DIR/MERGE_STASH" "$GIT_DIR/MERGE_MODE" || exit 1
|
||||
}
|
||||
|
||||
savestate() {
|
||||
# Stash away any local modifications.
|
||||
git stash create >"$GIT_DIR/MERGE_STASH"
|
||||
}
|
||||
|
||||
restorestate() {
|
||||
if test -f "$GIT_DIR/MERGE_STASH"
|
||||
then
|
||||
git reset --hard $head >/dev/null
|
||||
git stash apply $(cat "$GIT_DIR/MERGE_STASH")
|
||||
git update-index --refresh >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
finish_up_to_date () {
|
||||
case "$squash" in
|
||||
t)
|
||||
echo "$1 (nothing to squash)" ;;
|
||||
'')
|
||||
echo "$1" ;;
|
||||
esac
|
||||
dropsave
|
||||
}
|
||||
|
||||
squash_message () {
|
||||
echo Squashed commit of the following:
|
||||
echo
|
||||
git log --no-merges --pretty=medium ^"$head" $remoteheads
|
||||
}
|
||||
|
||||
finish () {
|
||||
if test '' = "$2"
|
||||
then
|
||||
rlogm="$GIT_REFLOG_ACTION"
|
||||
else
|
||||
echo "$2"
|
||||
rlogm="$GIT_REFLOG_ACTION: $2"
|
||||
fi
|
||||
case "$squash" in
|
||||
t)
|
||||
echo "Squash commit -- not updating HEAD"
|
||||
squash_message >"$GIT_DIR/SQUASH_MSG"
|
||||
;;
|
||||
'')
|
||||
case "$merge_msg" in
|
||||
'')
|
||||
echo "No merge message -- not updating HEAD"
|
||||
;;
|
||||
*)
|
||||
git update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1
|
||||
git gc --auto
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
case "$1" in
|
||||
'')
|
||||
;;
|
||||
?*)
|
||||
if test "$show_diffstat" = t
|
||||
then
|
||||
# We want color (if set), but no pager
|
||||
GIT_PAGER='' git diff --stat --summary -M "$head" "$1"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Run a post-merge hook
|
||||
if test -x "$GIT_DIR"/hooks/post-merge
|
||||
then
|
||||
case "$squash" in
|
||||
t)
|
||||
"$GIT_DIR"/hooks/post-merge 1
|
||||
;;
|
||||
'')
|
||||
"$GIT_DIR"/hooks/post-merge 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
merge_name () {
|
||||
remote="$1"
|
||||
rh=$(git rev-parse --verify "$remote^0" 2>/dev/null) || return
|
||||
if truname=$(expr "$remote" : '\(.*\)~[0-9]*$') &&
|
||||
git show-ref -q --verify "refs/heads/$truname" 2>/dev/null
|
||||
then
|
||||
echo "$rh branch '$truname' (early part) of ."
|
||||
return
|
||||
fi
|
||||
if found_ref=$(git rev-parse --symbolic-full-name --verify \
|
||||
"$remote" 2>/dev/null)
|
||||
then
|
||||
expanded=$(git check-ref-format --branch "$remote") ||
|
||||
exit
|
||||
if test "${found_ref#refs/heads/}" != "$found_ref"
|
||||
then
|
||||
echo "$rh branch '$expanded' of ."
|
||||
return
|
||||
elif test "${found_ref#refs/remotes/}" != "$found_ref"
|
||||
then
|
||||
echo "$rh remote branch '$expanded' of ."
|
||||
return
|
||||
fi
|
||||
fi
|
||||
if test "$remote" = "FETCH_HEAD" && test -r "$GIT_DIR/FETCH_HEAD"
|
||||
then
|
||||
sed -e 's/ not-for-merge / /' -e 1q \
|
||||
"$GIT_DIR/FETCH_HEAD"
|
||||
return
|
||||
fi
|
||||
echo "$rh commit '$remote'"
|
||||
}
|
||||
|
||||
parse_config () {
|
||||
while test $# != 0; do
|
||||
case "$1" in
|
||||
-n|--no-stat|--no-summary)
|
||||
show_diffstat=false ;;
|
||||
--stat|--summary)
|
||||
show_diffstat=t ;;
|
||||
--log|--no-log)
|
||||
log_arg=$1 ;;
|
||||
--squash)
|
||||
test "$allow_fast_forward" = t ||
|
||||
die "You cannot combine --squash with --no-ff."
|
||||
squash=t no_commit=t ;;
|
||||
--no-squash)
|
||||
squash= no_commit= ;;
|
||||
--commit)
|
||||
no_commit= ;;
|
||||
--no-commit)
|
||||
no_commit=t ;;
|
||||
--ff)
|
||||
allow_fast_forward=t ;;
|
||||
--no-ff)
|
||||
test "$squash" != t ||
|
||||
die "You cannot combine --squash with --no-ff."
|
||||
test "$fast_forward_only" != t ||
|
||||
die "You cannot combine --ff-only with --no-ff."
|
||||
allow_fast_forward=f ;;
|
||||
--ff-only)
|
||||
test "$allow_fast_forward" != f ||
|
||||
die "You cannot combine --ff-only with --no-ff."
|
||||
fast_forward_only=t ;;
|
||||
--rerere-autoupdate|--no-rerere-autoupdate)
|
||||
rr_arg=$1 ;;
|
||||
-s|--strategy)
|
||||
shift
|
||||
case " $all_strategies " in
|
||||
*" $1 "*)
|
||||
use_strategies="$use_strategies$1 "
|
||||
;;
|
||||
*)
|
||||
case " $not_strategies " in
|
||||
*" $1 "*)
|
||||
false
|
||||
esac &&
|
||||
type "git-merge-$1" >/dev/null 2>&1 ||
|
||||
die "available strategies are: $all_strategies"
|
||||
use_strategies="$use_strategies$1 "
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
-X)
|
||||
shift
|
||||
xopt="${xopt:+$xopt }$(git rev-parse --sq-quote "--$1")"
|
||||
;;
|
||||
-m|--message)
|
||||
shift
|
||||
merge_msg="$1"
|
||||
have_message=t
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
args_left=$#
|
||||
}
|
||||
|
||||
test $# != 0 || usage
|
||||
|
||||
have_message=
|
||||
|
||||
if branch=$(git-symbolic-ref -q HEAD)
|
||||
then
|
||||
mergeopts=$(git config "branch.${branch#refs/heads/}.mergeoptions")
|
||||
if test -n "$mergeopts"
|
||||
then
|
||||
parse_config $mergeopts --
|
||||
fi
|
||||
fi
|
||||
|
||||
parse_config "$@"
|
||||
while test $args_left -lt $#; do shift; done
|
||||
|
||||
if test -z "$show_diffstat"; then
|
||||
test "$(git config --bool merge.diffstat)" = false && show_diffstat=false
|
||||
test "$(git config --bool merge.stat)" = false && show_diffstat=false
|
||||
test -z "$show_diffstat" && show_diffstat=t
|
||||
fi
|
||||
|
||||
# This could be traditional "merge <msg> HEAD <commit>..." and the
|
||||
# way we can tell it is to see if the second token is HEAD, but some
|
||||
# people might have misused the interface and used a commit-ish that
|
||||
# is the same as HEAD there instead. Traditional format never would
|
||||
# have "-m" so it is an additional safety measure to check for it.
|
||||
|
||||
if test -z "$have_message" &&
|
||||
second_token=$(git rev-parse --verify "$2^0" 2>/dev/null) &&
|
||||
head_commit=$(git rev-parse --verify "HEAD" 2>/dev/null) &&
|
||||
test "$second_token" = "$head_commit"
|
||||
then
|
||||
merge_msg="$1"
|
||||
shift
|
||||
head_arg="$1"
|
||||
shift
|
||||
elif ! git rev-parse --verify HEAD >/dev/null 2>&1
|
||||
then
|
||||
# If the merged head is a valid one there is no reason to
|
||||
# forbid "git merge" into a branch yet to be born. We do
|
||||
# the same for "git pull".
|
||||
if test 1 -ne $#
|
||||
then
|
||||
echo >&2 "Can merge only exactly one commit into empty head"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
test "$squash" != t ||
|
||||
die "Squash commit into empty head not supported yet"
|
||||
test "$allow_fast_forward" = t ||
|
||||
die "Non-fast-forward into an empty head does not make sense"
|
||||
rh=$(git rev-parse --verify "$1^0") ||
|
||||
die "$1 - not something we can merge"
|
||||
|
||||
git update-ref -m "initial pull" HEAD "$rh" "" &&
|
||||
git read-tree --reset -u HEAD
|
||||
exit
|
||||
|
||||
else
|
||||
# We are invoked directly as the first-class UI.
|
||||
head_arg=HEAD
|
||||
|
||||
# All the rest are the commits being merged; prepare
|
||||
# the standard merge summary message to be appended to
|
||||
# the given message. If remote is invalid we will die
|
||||
# later in the common codepath so we discard the error
|
||||
# in this loop.
|
||||
merge_msg="$(
|
||||
for remote
|
||||
do
|
||||
merge_name "$remote"
|
||||
done |
|
||||
if test "$have_message" = t
|
||||
then
|
||||
git fmt-merge-msg -m "$merge_msg" $log_arg
|
||||
else
|
||||
git fmt-merge-msg $log_arg
|
||||
fi
|
||||
)"
|
||||
fi
|
||||
head=$(git rev-parse --verify "$head_arg"^0) || usage
|
||||
|
||||
# All the rest are remote heads
|
||||
test "$#" = 0 && usage ;# we need at least one remote head.
|
||||
set_reflog_action "merge $*"
|
||||
|
||||
remoteheads=
|
||||
for remote
|
||||
do
|
||||
remotehead=$(git rev-parse --verify "$remote"^0 2>/dev/null) ||
|
||||
die "$remote - not something we can merge"
|
||||
remoteheads="${remoteheads}$remotehead "
|
||||
eval GITHEAD_$remotehead='"$remote"'
|
||||
export GITHEAD_$remotehead
|
||||
done
|
||||
set x $remoteheads ; shift
|
||||
|
||||
case "$use_strategies" in
|
||||
'')
|
||||
case "$#" in
|
||||
1)
|
||||
var="$(git config --get pull.twohead)"
|
||||
if test -n "$var"
|
||||
then
|
||||
use_strategies="$var"
|
||||
else
|
||||
use_strategies="$default_twohead_strategies"
|
||||
fi ;;
|
||||
*)
|
||||
var="$(git config --get pull.octopus)"
|
||||
if test -n "$var"
|
||||
then
|
||||
use_strategies="$var"
|
||||
else
|
||||
use_strategies="$default_octopus_strategies"
|
||||
fi ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
for s in $use_strategies
|
||||
do
|
||||
for ss in $no_fast_forward_strategies
|
||||
do
|
||||
case " $s " in
|
||||
*" $ss "*)
|
||||
allow_fast_forward=f
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
for ss in $no_trivial_strategies
|
||||
do
|
||||
case " $s " in
|
||||
*" $ss "*)
|
||||
allow_trivial_merge=f
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
|
||||
case "$#" in
|
||||
1)
|
||||
common=$(git merge-base --all $head "$@")
|
||||
;;
|
||||
*)
|
||||
common=$(git merge-base --all --octopus $head "$@")
|
||||
;;
|
||||
esac
|
||||
echo "$head" >"$GIT_DIR/ORIG_HEAD"
|
||||
|
||||
case "$allow_fast_forward,$#,$common,$no_commit" in
|
||||
?,*,'',*)
|
||||
# No common ancestors found. We need a real merge.
|
||||
;;
|
||||
?,1,"$1",*)
|
||||
# If head can reach all the merge then we are up to date.
|
||||
# but first the most common case of merging one remote.
|
||||
finish_up_to_date "Already up to date."
|
||||
exit 0
|
||||
;;
|
||||
t,1,"$head",*)
|
||||
# Again the most common case of merging one remote.
|
||||
echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $1)"
|
||||
git update-index --refresh 2>/dev/null
|
||||
msg="Fast-forward"
|
||||
if test -n "$have_message"
|
||||
then
|
||||
msg="$msg (no commit created; -m option ignored)"
|
||||
fi
|
||||
new_head=$(git rev-parse --verify "$1^0") &&
|
||||
git read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" &&
|
||||
finish "$new_head" "$msg" || exit
|
||||
dropsave
|
||||
exit 0
|
||||
;;
|
||||
?,1,?*"$LF"?*,*)
|
||||
# We are not doing octopus and not fast-forward. Need a
|
||||
# real merge.
|
||||
;;
|
||||
?,1,*,)
|
||||
# We are not doing octopus, not fast-forward, and have only
|
||||
# one common.
|
||||
git update-index --refresh 2>/dev/null
|
||||
case "$allow_trivial_merge,$fast_forward_only" in
|
||||
t,)
|
||||
# See if it is really trivial.
|
||||
git var GIT_COMMITTER_IDENT >/dev/null || exit
|
||||
echo "Trying really trivial in-index merge..."
|
||||
if git read-tree --trivial -m -u -v $common $head "$1" &&
|
||||
result_tree=$(git write-tree)
|
||||
then
|
||||
echo "Wonderful."
|
||||
result_commit=$(
|
||||
printf '%s\n' "$merge_msg" |
|
||||
git commit-tree $result_tree -p HEAD -p "$1"
|
||||
) || exit
|
||||
finish "$result_commit" "In-index merge"
|
||||
dropsave
|
||||
exit 0
|
||||
fi
|
||||
echo "Nope."
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
# An octopus. If we can reach all the remote we are up to date.
|
||||
up_to_date=t
|
||||
for remote
|
||||
do
|
||||
common_one=$(git merge-base --all $head $remote)
|
||||
if test "$common_one" != "$remote"
|
||||
then
|
||||
up_to_date=f
|
||||
break
|
||||
fi
|
||||
done
|
||||
if test "$up_to_date" = t
|
||||
then
|
||||
finish_up_to_date "Already up to date. Yeeah!"
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if test "$fast_forward_only" = t
|
||||
then
|
||||
die "Not possible to fast-forward, aborting."
|
||||
fi
|
||||
|
||||
# We are going to make a new commit.
|
||||
git var GIT_COMMITTER_IDENT >/dev/null || exit
|
||||
|
||||
# At this point, we need a real merge. No matter what strategy
|
||||
# we use, it would operate on the index, possibly affecting the
|
||||
# working tree, and when resolved cleanly, have the desired tree
|
||||
# in the index -- this means that the index must be in sync with
|
||||
# the $head commit. The strategies are responsible to ensure this.
|
||||
|
||||
case "$use_strategies" in
|
||||
?*' '?*)
|
||||
# Stash away the local changes so that we can try more than one.
|
||||
savestate
|
||||
single_strategy=no
|
||||
;;
|
||||
*)
|
||||
rm -f "$GIT_DIR/MERGE_STASH"
|
||||
single_strategy=yes
|
||||
;;
|
||||
esac
|
||||
|
||||
result_tree= best_cnt=-1 best_strategy= wt_strategy=
|
||||
merge_was_ok=
|
||||
for strategy in $use_strategies
|
||||
do
|
||||
test "$wt_strategy" = '' || {
|
||||
echo "Rewinding the tree to pristine..."
|
||||
restorestate
|
||||
}
|
||||
case "$single_strategy" in
|
||||
no)
|
||||
echo "Trying merge strategy $strategy..."
|
||||
;;
|
||||
esac
|
||||
|
||||
# Remember which strategy left the state in the working tree
|
||||
wt_strategy=$strategy
|
||||
|
||||
eval 'git-merge-$strategy '"$xopt"' $common -- "$head_arg" "$@"'
|
||||
exit=$?
|
||||
if test "$no_commit" = t && test "$exit" = 0
|
||||
then
|
||||
merge_was_ok=t
|
||||
exit=1 ;# pretend it left conflicts.
|
||||
fi
|
||||
|
||||
test "$exit" = 0 || {
|
||||
|
||||
# The backend exits with 1 when conflicts are left to be resolved,
|
||||
# with 2 when it does not handle the given merge at all.
|
||||
|
||||
if test "$exit" -eq 1
|
||||
then
|
||||
cnt=$({
|
||||
git diff-files --name-only
|
||||
git ls-files --unmerged
|
||||
} | wc -l)
|
||||
if test $best_cnt -le 0 || test $cnt -le $best_cnt
|
||||
then
|
||||
best_strategy=$strategy
|
||||
best_cnt=$cnt
|
||||
fi
|
||||
fi
|
||||
continue
|
||||
}
|
||||
|
||||
# Automerge succeeded.
|
||||
result_tree=$(git write-tree) && break
|
||||
done
|
||||
|
||||
# If we have a resulting tree, that means the strategy module
|
||||
# auto resolved the merge cleanly.
|
||||
if test '' != "$result_tree"
|
||||
then
|
||||
if test "$allow_fast_forward" = "t"
|
||||
then
|
||||
parents=$(git merge-base --independent "$head" "$@")
|
||||
else
|
||||
parents=$(git rev-parse "$head" "$@")
|
||||
fi
|
||||
parents=$(echo "$parents" | sed -e 's/^/-p /')
|
||||
result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit
|
||||
finish "$result_commit" "Merge made by $wt_strategy."
|
||||
dropsave
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Pick the result from the best strategy and have the user fix it up.
|
||||
case "$best_strategy" in
|
||||
'')
|
||||
restorestate
|
||||
case "$use_strategies" in
|
||||
?*' '?*)
|
||||
echo >&2 "No merge strategy handled the merge."
|
||||
;;
|
||||
*)
|
||||
echo >&2 "Merge with strategy $use_strategies failed."
|
||||
;;
|
||||
esac
|
||||
exit 2
|
||||
;;
|
||||
"$wt_strategy")
|
||||
# We already have its result in the working tree.
|
||||
;;
|
||||
*)
|
||||
echo "Rewinding the tree to pristine..."
|
||||
restorestate
|
||||
echo "Using the $best_strategy to prepare resolving by hand."
|
||||
git-merge-$best_strategy $common -- "$head_arg" "$@"
|
||||
;;
|
||||
esac
|
||||
|
||||
if test "$squash" = t
|
||||
then
|
||||
finish
|
||||
else
|
||||
for remote
|
||||
do
|
||||
echo $remote
|
||||
done >"$GIT_DIR/MERGE_HEAD"
|
||||
printf '%s\n' "$merge_msg" >"$GIT_DIR/MERGE_MSG" ||
|
||||
die "Could not write to $GIT_DIR/MERGE_MSG"
|
||||
if test "$allow_fast_forward" != t
|
||||
then
|
||||
printf "%s" no-ff
|
||||
else
|
||||
:
|
||||
fi >"$GIT_DIR/MERGE_MODE" ||
|
||||
die "Could not write to $GIT_DIR/MERGE_MODE"
|
||||
fi
|
||||
|
||||
if test "$merge_was_ok" = t
|
||||
then
|
||||
echo >&2 \
|
||||
"Automatic merge went well; stopped before committing as requested"
|
||||
exit 0
|
||||
else
|
||||
{
|
||||
echo '
|
||||
Conflicts:
|
||||
'
|
||||
git ls-files --unmerged |
|
||||
sed -e 's/^[^ ]* / /' |
|
||||
uniq
|
||||
} >>"$GIT_DIR/MERGE_MSG"
|
||||
git rerere $rr_arg
|
||||
die "Automatic merge failed; fix conflicts and then commit the result."
|
||||
fi
|
|
@ -1,121 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
USAGE="(edit [-F <file> | -m <msg>] | show) [commit]"
|
||||
. git-sh-setup
|
||||
|
||||
test -z "$1" && usage
|
||||
ACTION="$1"; shift
|
||||
|
||||
test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="$(git config core.notesref)"
|
||||
test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="refs/notes/commits"
|
||||
|
||||
MESSAGE=
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-m)
|
||||
test "$ACTION" = "edit" || usage
|
||||
shift
|
||||
if test "$#" = "0"; then
|
||||
die "error: option -m needs an argument"
|
||||
else
|
||||
if [ -z "$MESSAGE" ]; then
|
||||
MESSAGE="$1"
|
||||
else
|
||||
MESSAGE="$MESSAGE
|
||||
|
||||
$1"
|
||||
fi
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
-F)
|
||||
test "$ACTION" = "edit" || usage
|
||||
shift
|
||||
if test "$#" = "0"; then
|
||||
die "error: option -F needs an argument"
|
||||
else
|
||||
if [ -z "$MESSAGE" ]; then
|
||||
MESSAGE="$(cat "$1")"
|
||||
else
|
||||
MESSAGE="$MESSAGE
|
||||
|
||||
$(cat "$1")"
|
||||
fi
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
COMMIT=$(git rev-parse --verify --default HEAD "$@") ||
|
||||
die "Invalid commit: $@"
|
||||
|
||||
case "$ACTION" in
|
||||
edit)
|
||||
if [ "${GIT_NOTES_REF#refs/notes/}" = "$GIT_NOTES_REF" ]; then
|
||||
die "Refusing to edit notes in $GIT_NOTES_REF (outside of refs/notes/)"
|
||||
fi
|
||||
|
||||
MSG_FILE="$GIT_DIR/new-notes-$COMMIT"
|
||||
GIT_INDEX_FILE="$MSG_FILE.idx"
|
||||
export GIT_INDEX_FILE
|
||||
|
||||
trap '
|
||||
test -f "$MSG_FILE" && rm "$MSG_FILE"
|
||||
test -f "$GIT_INDEX_FILE" && rm "$GIT_INDEX_FILE"
|
||||
' 0
|
||||
|
||||
CURRENT_HEAD=$(git show-ref "$GIT_NOTES_REF" | cut -f 1 -d ' ')
|
||||
if [ -z "$CURRENT_HEAD" ]; then
|
||||
PARENT=
|
||||
else
|
||||
PARENT="-p $CURRENT_HEAD"
|
||||
git read-tree "$GIT_NOTES_REF" || die "Could not read index"
|
||||
fi
|
||||
|
||||
if [ -z "$MESSAGE" ]; then
|
||||
GIT_NOTES_REF= git log -1 $COMMIT | sed "s/^/#/" > "$MSG_FILE"
|
||||
if [ ! -z "$CURRENT_HEAD" ]; then
|
||||
git cat-file blob :$COMMIT >> "$MSG_FILE" 2> /dev/null
|
||||
fi
|
||||
core_editor="$(git config core.editor)"
|
||||
${GIT_EDITOR:-${core_editor:-${VISUAL:-${EDITOR:-vi}}}} "$MSG_FILE"
|
||||
else
|
||||
echo "$MESSAGE" > "$MSG_FILE"
|
||||
fi
|
||||
|
||||
grep -v ^# < "$MSG_FILE" | git stripspace > "$MSG_FILE".processed
|
||||
mv "$MSG_FILE".processed "$MSG_FILE"
|
||||
if [ -s "$MSG_FILE" ]; then
|
||||
BLOB=$(git hash-object -w "$MSG_FILE") ||
|
||||
die "Could not write into object database"
|
||||
git update-index --add --cacheinfo 0644 $BLOB $COMMIT ||
|
||||
die "Could not write index"
|
||||
else
|
||||
test -z "$CURRENT_HEAD" &&
|
||||
die "Will not initialise with empty tree"
|
||||
git update-index --force-remove $COMMIT ||
|
||||
die "Could not update index"
|
||||
fi
|
||||
|
||||
TREE=$(git write-tree) || die "Could not write tree"
|
||||
NEW_HEAD=$(echo Annotate $COMMIT | git commit-tree $TREE $PARENT) ||
|
||||
die "Could not annotate"
|
||||
git update-ref -m "Annotate $COMMIT" \
|
||||
"$GIT_NOTES_REF" $NEW_HEAD $CURRENT_HEAD
|
||||
;;
|
||||
show)
|
||||
git rev-parse -q --verify "$GIT_NOTES_REF":$COMMIT > /dev/null ||
|
||||
die "No note for commit $COMMIT."
|
||||
git show "$GIT_NOTES_REF":$COMMIT
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
esac
|
|
@ -1,381 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Junio C Hamano
|
||||
#
|
||||
# Fetch one or more remote refs and merge it/them into the current HEAD.
|
||||
|
||||
SUBDIRECTORY_OK=Yes
|
||||
OPTIONS_KEEPDASHDASH=
|
||||
OPTIONS_STUCKLONG=Yes
|
||||
OPTIONS_SPEC="\
|
||||
git pull [options] [<repository> [<refspec>...]]
|
||||
|
||||
Fetch one or more remote refs and integrate it/them with the current HEAD.
|
||||
--
|
||||
v,verbose be more verbose
|
||||
q,quiet be more quiet
|
||||
progress force progress reporting
|
||||
|
||||
Options related to merging
|
||||
r,rebase?false|true|preserve incorporate changes by rebasing rather than merging
|
||||
n! do not show a diffstat at the end of the merge
|
||||
stat show a diffstat at the end of the merge
|
||||
summary (synonym to --stat)
|
||||
log?n add (at most <n>) entries from shortlog to merge commit message
|
||||
squash create a single commit instead of doing a merge
|
||||
commit perform a commit if the merge succeeds (default)
|
||||
e,edit edit message before committing
|
||||
ff allow fast-forward
|
||||
ff-only! abort if fast-forward is not possible
|
||||
verify-signatures verify that the named commit has a valid GPG signature
|
||||
s,strategy=strategy merge strategy to use
|
||||
X,strategy-option=option option for selected merge strategy
|
||||
S,gpg-sign?key-id GPG sign commit
|
||||
|
||||
Options related to fetching
|
||||
all fetch from all remotes
|
||||
a,append append to .git/FETCH_HEAD instead of overwriting
|
||||
upload-pack=path path to upload pack on remote end
|
||||
f,force force overwrite of local branch
|
||||
t,tags fetch all tags and associated objects
|
||||
p,prune prune remote-tracking branches no longer on remote
|
||||
recurse-submodules?on-demand control recursive fetching of submodules
|
||||
dry-run dry run
|
||||
k,keep keep downloaded pack
|
||||
depth=depth deepen history of shallow clone
|
||||
unshallow convert to a complete repository
|
||||
update-shallow accept refs that update .git/shallow
|
||||
refmap=refmap specify fetch refmap
|
||||
"
|
||||
test $# -gt 0 && args="$*"
|
||||
. git-sh-setup
|
||||
. git-sh-i18n
|
||||
set_reflog_action "pull${args+ $args}"
|
||||
require_work_tree_exists
|
||||
cd_to_toplevel
|
||||
|
||||
|
||||
die_conflict () {
|
||||
git diff-index --cached --name-status -r --ignore-submodules HEAD --
|
||||
if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then
|
||||
die "$(gettext "Pull is not possible because you have unmerged files.
|
||||
Please, fix them up in the work tree, and then use 'git add/rm <file>'
|
||||
as appropriate to mark resolution and make a commit.")"
|
||||
else
|
||||
die "$(gettext "Pull is not possible because you have unmerged files.")"
|
||||
fi
|
||||
}
|
||||
|
||||
die_merge () {
|
||||
if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then
|
||||
die "$(gettext "You have not concluded your merge (MERGE_HEAD exists).
|
||||
Please, commit your changes before merging.")"
|
||||
else
|
||||
die "$(gettext "You have not concluded your merge (MERGE_HEAD exists).")"
|
||||
fi
|
||||
}
|
||||
|
||||
test -z "$(git ls-files -u)" || die_conflict
|
||||
test -f "$GIT_DIR/MERGE_HEAD" && die_merge
|
||||
|
||||
bool_or_string_config () {
|
||||
git config --bool "$1" 2>/dev/null || git config "$1"
|
||||
}
|
||||
|
||||
strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
|
||||
log_arg= verbosity= progress= recurse_submodules= verify_signatures=
|
||||
merge_args= edit= rebase_args= all= append= upload_pack= force= tags= prune=
|
||||
keep= depth= unshallow= update_shallow= refmap=
|
||||
curr_branch=$(git symbolic-ref -q HEAD)
|
||||
curr_branch_short="${curr_branch#refs/heads/}"
|
||||
rebase=$(bool_or_string_config branch.$curr_branch_short.rebase)
|
||||
if test -z "$rebase"
|
||||
then
|
||||
rebase=$(bool_or_string_config pull.rebase)
|
||||
fi
|
||||
|
||||
# Setup default fast-forward options via `pull.ff`
|
||||
pull_ff=$(bool_or_string_config pull.ff)
|
||||
case "$pull_ff" in
|
||||
true)
|
||||
no_ff=--ff
|
||||
;;
|
||||
false)
|
||||
no_ff=--no-ff
|
||||
;;
|
||||
only)
|
||||
ff_only=--ff-only
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
dry_run=
|
||||
while :
|
||||
do
|
||||
case "$1" in
|
||||
-q|--quiet)
|
||||
verbosity="$verbosity -q" ;;
|
||||
-v|--verbose)
|
||||
verbosity="$verbosity -v" ;;
|
||||
--progress)
|
||||
progress=--progress ;;
|
||||
--no-progress)
|
||||
progress=--no-progress ;;
|
||||
-n|--no-stat|--no-summary)
|
||||
diffstat=--no-stat ;;
|
||||
--stat|--summary)
|
||||
diffstat=--stat ;;
|
||||
--log|--log=*|--no-log)
|
||||
log_arg="$1" ;;
|
||||
--no-commit)
|
||||
no_commit=--no-commit ;;
|
||||
--commit)
|
||||
no_commit=--commit ;;
|
||||
-e|--edit)
|
||||
edit=--edit ;;
|
||||
--no-edit)
|
||||
edit=--no-edit ;;
|
||||
--squash)
|
||||
squash=--squash ;;
|
||||
--no-squash)
|
||||
squash=--no-squash ;;
|
||||
--ff)
|
||||
no_ff=--ff ;;
|
||||
--no-ff)
|
||||
no_ff=--no-ff ;;
|
||||
--ff-only)
|
||||
ff_only=--ff-only ;;
|
||||
-s*|--strategy=*)
|
||||
strategy_args="$strategy_args $1"
|
||||
;;
|
||||
-X*|--strategy-option=*)
|
||||
merge_args="$merge_args $(git rev-parse --sq-quote "$1")"
|
||||
;;
|
||||
-r*|--rebase=*)
|
||||
rebase="${1#*=}"
|
||||
;;
|
||||
--rebase)
|
||||
rebase=true
|
||||
;;
|
||||
--no-rebase)
|
||||
rebase=false
|
||||
;;
|
||||
--recurse-submodules)
|
||||
recurse_submodules=--recurse-submodules
|
||||
;;
|
||||
--recurse-submodules=*)
|
||||
recurse_submodules="$1"
|
||||
;;
|
||||
--no-recurse-submodules)
|
||||
recurse_submodules=--no-recurse-submodules
|
||||
;;
|
||||
--verify-signatures)
|
||||
verify_signatures=--verify-signatures
|
||||
;;
|
||||
--no-verify-signatures)
|
||||
verify_signatures=--no-verify-signatures
|
||||
;;
|
||||
--gpg-sign|-S)
|
||||
gpg_sign_args=-S
|
||||
;;
|
||||
--gpg-sign=*)
|
||||
gpg_sign_args=$(git rev-parse --sq-quote "-S${1#--gpg-sign=}")
|
||||
;;
|
||||
-S*)
|
||||
gpg_sign_args=$(git rev-parse --sq-quote "$1")
|
||||
;;
|
||||
--dry-run)
|
||||
dry_run=--dry-run
|
||||
;;
|
||||
--all|--no-all)
|
||||
all=$1 ;;
|
||||
-a|--append|--no-append)
|
||||
append=$1 ;;
|
||||
--upload-pack=*|--no-upload-pack)
|
||||
upload_pack=$1 ;;
|
||||
-f|--force|--no-force)
|
||||
force="$force $1" ;;
|
||||
-t|--tags|--no-tags)
|
||||
tags=$1 ;;
|
||||
-p|--prune|--no-prune)
|
||||
prune=$1 ;;
|
||||
-k|--keep|--no-keep)
|
||||
keep=$1 ;;
|
||||
--depth=*|--no-depth)
|
||||
depth=$1 ;;
|
||||
--unshallow|--no-unshallow)
|
||||
unshallow=$1 ;;
|
||||
--update-shallow|--no-update-shallow)
|
||||
update_shallow=$1 ;;
|
||||
--refmap=*|--no-refmap)
|
||||
refmap=$1 ;;
|
||||
-h|--help-all)
|
||||
usage
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
case "$rebase" in
|
||||
preserve)
|
||||
rebase=true
|
||||
rebase_args=--preserve-merges
|
||||
;;
|
||||
true|false|'')
|
||||
;;
|
||||
*)
|
||||
echo "Invalid value for --rebase, should be true, false, or preserve"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
error_on_no_merge_candidates () {
|
||||
exec >&2
|
||||
|
||||
if test true = "$rebase"
|
||||
then
|
||||
op_type=rebase
|
||||
op_prep=against
|
||||
else
|
||||
op_type=merge
|
||||
op_prep=with
|
||||
fi
|
||||
|
||||
upstream=$(git config "branch.$curr_branch_short.merge")
|
||||
remote=$(git config "branch.$curr_branch_short.remote")
|
||||
|
||||
if [ $# -gt 1 ]; then
|
||||
if [ "$rebase" = true ]; then
|
||||
printf "There is no candidate for rebasing against "
|
||||
else
|
||||
printf "There are no candidates for merging "
|
||||
fi
|
||||
echo "among the refs that you just fetched."
|
||||
echo "Generally this means that you provided a wildcard refspec which had no"
|
||||
echo "matches on the remote end."
|
||||
elif [ $# -gt 0 ] && [ "$1" != "$remote" ]; then
|
||||
echo "You asked to pull from the remote '$1', but did not specify"
|
||||
echo "a branch. Because this is not the default configured remote"
|
||||
echo "for your current branch, you must specify a branch on the command line."
|
||||
elif [ -z "$curr_branch" -o -z "$upstream" ]; then
|
||||
. git-parse-remote
|
||||
error_on_missing_default_upstream "pull" $op_type $op_prep \
|
||||
"git pull <remote> <branch>"
|
||||
else
|
||||
echo "Your configuration specifies to $op_type $op_prep the ref '${upstream#refs/heads/}'"
|
||||
echo "from the remote, but no such ref was fetched."
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
test true = "$rebase" && {
|
||||
if ! git rev-parse -q --verify HEAD >/dev/null
|
||||
then
|
||||
# On an unborn branch
|
||||
if test -f "$(git rev-parse --git-path index)"
|
||||
then
|
||||
die "$(gettext "updating an unborn branch with changes added to the index")"
|
||||
fi
|
||||
else
|
||||
require_clean_work_tree "pull with rebase" "Please commit or stash them."
|
||||
fi
|
||||
oldremoteref= &&
|
||||
test -n "$curr_branch" &&
|
||||
. git-parse-remote &&
|
||||
remoteref="$(get_remote_merge_branch "$@" 2>/dev/null)" &&
|
||||
oldremoteref=$(git merge-base --fork-point "$remoteref" $curr_branch 2>/dev/null)
|
||||
}
|
||||
orig_head=$(git rev-parse -q --verify HEAD)
|
||||
git fetch $verbosity $progress $dry_run $recurse_submodules $all $append \
|
||||
${upload_pack:+"$upload_pack"} $force $tags $prune $keep $depth $unshallow $update_shallow \
|
||||
$refmap --update-head-ok "$@" || exit 1
|
||||
test -z "$dry_run" || exit 0
|
||||
|
||||
curr_head=$(git rev-parse -q --verify HEAD)
|
||||
if test -n "$orig_head" && test "$curr_head" != "$orig_head"
|
||||
then
|
||||
# The fetch involved updating the current branch.
|
||||
|
||||
# The working tree and the index file is still based on the
|
||||
# $orig_head commit, but we are merging into $curr_head.
|
||||
# First update the working tree to match $curr_head.
|
||||
|
||||
eval_gettextln "Warning: fetch updated the current branch head.
|
||||
Warning: fast-forwarding your working tree from
|
||||
Warning: commit \$orig_head." >&2
|
||||
git update-index -q --refresh
|
||||
git read-tree -u -m "$orig_head" "$curr_head" ||
|
||||
die "$(eval_gettext "Cannot fast-forward your working tree.
|
||||
After making sure that you saved anything precious from
|
||||
$ git diff \$orig_head
|
||||
output, run
|
||||
$ git reset --hard
|
||||
to recover.")"
|
||||
|
||||
fi
|
||||
|
||||
merge_head=$(sed -e '/ not-for-merge /d' \
|
||||
-e 's/ .*//' "$GIT_DIR"/FETCH_HEAD | \
|
||||
tr '\012' ' ')
|
||||
|
||||
case "$merge_head" in
|
||||
'')
|
||||
error_on_no_merge_candidates "$@"
|
||||
;;
|
||||
?*' '?*)
|
||||
if test -z "$orig_head"
|
||||
then
|
||||
die "$(gettext "Cannot merge multiple branches into empty head")"
|
||||
fi
|
||||
if test true = "$rebase"
|
||||
then
|
||||
die "$(gettext "Cannot rebase onto multiple branches")"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Pulling into unborn branch: a shorthand for branching off
|
||||
# FETCH_HEAD, for lazy typers.
|
||||
if test -z "$orig_head"
|
||||
then
|
||||
# Two-way merge: we claim the index is based on an empty tree,
|
||||
# and try to fast-forward to HEAD. This ensures we will not
|
||||
# lose index/worktree changes that the user already made on
|
||||
# the unborn branch.
|
||||
empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
|
||||
git read-tree -m -u $empty_tree $merge_head &&
|
||||
git update-ref -m "initial pull" HEAD $merge_head "$curr_head"
|
||||
exit
|
||||
fi
|
||||
|
||||
if test true = "$rebase"
|
||||
then
|
||||
o=$(git show-branch --merge-base $curr_branch $merge_head $oldremoteref)
|
||||
if test "$oldremoteref" = "$o"
|
||||
then
|
||||
unset oldremoteref
|
||||
fi
|
||||
fi
|
||||
|
||||
case "$rebase" in
|
||||
true)
|
||||
eval="git-rebase $diffstat $strategy_args $merge_args $rebase_args $verbosity"
|
||||
eval="$eval $gpg_sign_args"
|
||||
eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}"
|
||||
;;
|
||||
*)
|
||||
eval="git-merge $diffstat $no_commit $verify_signatures $edit $squash $no_ff $ff_only"
|
||||
eval="$eval $log_arg $strategy_args $merge_args $verbosity $progress"
|
||||
eval="$eval $gpg_sign_args"
|
||||
eval="$eval FETCH_HEAD"
|
||||
;;
|
||||
esac
|
||||
eval "exec $eval"
|
|
@ -1,474 +0,0 @@
|
|||
#!/usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
use Git;
|
||||
my $git = Git->repository();
|
||||
|
||||
sub add_remote_config {
|
||||
my ($hash, $name, $what, $value) = @_;
|
||||
if ($what eq 'url') {
|
||||
# Having more than one is Ok -- it is used for push.
|
||||
if (! exists $hash->{'URL'}) {
|
||||
$hash->{$name}{'URL'} = $value;
|
||||
}
|
||||
}
|
||||
elsif ($what eq 'fetch') {
|
||||
$hash->{$name}{'FETCH'} ||= [];
|
||||
push @{$hash->{$name}{'FETCH'}}, $value;
|
||||
}
|
||||
elsif ($what eq 'push') {
|
||||
$hash->{$name}{'PUSH'} ||= [];
|
||||
push @{$hash->{$name}{'PUSH'}}, $value;
|
||||
}
|
||||
if (!exists $hash->{$name}{'SOURCE'}) {
|
||||
$hash->{$name}{'SOURCE'} = 'config';
|
||||
}
|
||||
}
|
||||
|
||||
sub add_remote_remotes {
|
||||
my ($hash, $file, $name) = @_;
|
||||
|
||||
if (exists $hash->{$name}) {
|
||||
$hash->{$name}{'WARNING'} = 'ignored due to config';
|
||||
return;
|
||||
}
|
||||
|
||||
my $fh;
|
||||
if (!open($fh, '<', $file)) {
|
||||
print STDERR "Warning: cannot open $file\n";
|
||||
return;
|
||||
}
|
||||
my $it = { 'SOURCE' => 'remotes' };
|
||||
$hash->{$name} = $it;
|
||||
while (<$fh>) {
|
||||
chomp;
|
||||
if (/^URL:\s*(.*)$/) {
|
||||
# Having more than one is Ok -- it is used for push.
|
||||
if (! exists $it->{'URL'}) {
|
||||
$it->{'URL'} = $1;
|
||||
}
|
||||
}
|
||||
elsif (/^Push:\s*(.*)$/) {
|
||||
$it->{'PUSH'} ||= [];
|
||||
push @{$it->{'PUSH'}}, $1;
|
||||
}
|
||||
elsif (/^Pull:\s*(.*)$/) {
|
||||
$it->{'FETCH'} ||= [];
|
||||
push @{$it->{'FETCH'}}, $1;
|
||||
}
|
||||
elsif (/^\#/) {
|
||||
; # ignore
|
||||
}
|
||||
else {
|
||||
print STDERR "Warning: funny line in $file: $_\n";
|
||||
}
|
||||
}
|
||||
close($fh);
|
||||
}
|
||||
|
||||
sub list_remote {
|
||||
my ($git) = @_;
|
||||
my %seen = ();
|
||||
my @remotes = eval {
|
||||
$git->command(qw(config --get-regexp), '^remote\.');
|
||||
};
|
||||
for (@remotes) {
|
||||
if (/^remote\.(\S+?)\.([^.\s]+)\s+(.*)$/) {
|
||||
add_remote_config(\%seen, $1, $2, $3);
|
||||
}
|
||||
}
|
||||
|
||||
my $dir = $git->repo_path() . "/remotes";
|
||||
if (opendir(my $dh, $dir)) {
|
||||
local $_;
|
||||
while ($_ = readdir($dh)) {
|
||||
chomp;
|
||||
next if (! -f "$dir/$_" || ! -r _);
|
||||
add_remote_remotes(\%seen, "$dir/$_", $_);
|
||||
}
|
||||
}
|
||||
|
||||
return \%seen;
|
||||
}
|
||||
|
||||
sub add_branch_config {
|
||||
my ($hash, $name, $what, $value) = @_;
|
||||
if ($what eq 'remote') {
|
||||
if (exists $hash->{$name}{'REMOTE'}) {
|
||||
print STDERR "Warning: more than one branch.$name.remote\n";
|
||||
}
|
||||
$hash->{$name}{'REMOTE'} = $value;
|
||||
}
|
||||
elsif ($what eq 'merge') {
|
||||
$hash->{$name}{'MERGE'} ||= [];
|
||||
push @{$hash->{$name}{'MERGE'}}, $value;
|
||||
}
|
||||
}
|
||||
|
||||
sub list_branch {
|
||||
my ($git) = @_;
|
||||
my %seen = ();
|
||||
my @branches = eval {
|
||||
$git->command(qw(config --get-regexp), '^branch\.');
|
||||
};
|
||||
for (@branches) {
|
||||
if (/^branch\.([^.]*)\.(\S*)\s+(.*)$/) {
|
||||
add_branch_config(\%seen, $1, $2, $3);
|
||||
}
|
||||
}
|
||||
|
||||
return \%seen;
|
||||
}
|
||||
|
||||
my $remote = list_remote($git);
|
||||
my $branch = list_branch($git);
|
||||
|
||||
sub update_ls_remote {
|
||||
my ($harder, $info) = @_;
|
||||
|
||||
return if (($harder == 0) ||
|
||||
(($harder == 1) && exists $info->{'LS_REMOTE'}));
|
||||
|
||||
my @ref = map { s|refs/heads/||; $_; } keys %{$git->remote_refs($info->{'URL'}, [ 'heads' ])};
|
||||
$info->{'LS_REMOTE'} = \@ref;
|
||||
}
|
||||
|
||||
sub list_wildcard_mapping {
|
||||
my ($forced, $ours, $ls) = @_;
|
||||
my %refs;
|
||||
for (@$ls) {
|
||||
$refs{$_} = 01; # bit #0 to say "they have"
|
||||
}
|
||||
for ($git->command('for-each-ref', "refs/remotes/$ours")) {
|
||||
chomp;
|
||||
next unless (s|^[0-9a-f]{40}\s[a-z]+\srefs/remotes/$ours/||);
|
||||
next if ($_ eq 'HEAD');
|
||||
$refs{$_} ||= 0;
|
||||
$refs{$_} |= 02; # bit #1 to say "we have"
|
||||
}
|
||||
my (@new, @stale, @tracked);
|
||||
for (sort keys %refs) {
|
||||
my $have = $refs{$_};
|
||||
if ($have == 1) {
|
||||
push @new, $_;
|
||||
}
|
||||
elsif ($have == 2) {
|
||||
push @stale, $_;
|
||||
}
|
||||
elsif ($have == 3) {
|
||||
push @tracked, $_;
|
||||
}
|
||||
}
|
||||
return \@new, \@stale, \@tracked;
|
||||
}
|
||||
|
||||
sub list_mapping {
|
||||
my ($name, $info) = @_;
|
||||
my $fetch = $info->{'FETCH'};
|
||||
my $ls = $info->{'LS_REMOTE'};
|
||||
my (@new, @stale, @tracked);
|
||||
|
||||
for (@$fetch) {
|
||||
next unless (/(\+)?([^:]+):(.*)/);
|
||||
my ($forced, $theirs, $ours) = ($1, $2, $3);
|
||||
if ($theirs eq 'refs/heads/*' &&
|
||||
$ours =~ /^refs\/remotes\/(.*)\/\*$/) {
|
||||
# wildcard mapping
|
||||
my ($w_new, $w_stale, $w_tracked)
|
||||
= list_wildcard_mapping($forced, $1, $ls);
|
||||
push @new, @$w_new;
|
||||
push @stale, @$w_stale;
|
||||
push @tracked, @$w_tracked;
|
||||
}
|
||||
elsif ($theirs =~ /\*/ || $ours =~ /\*/) {
|
||||
print STDERR "Warning: unrecognized mapping in remotes.$name.fetch: $_\n";
|
||||
}
|
||||
elsif ($theirs =~ s|^refs/heads/||) {
|
||||
if (!grep { $_ eq $theirs } @$ls) {
|
||||
push @stale, $theirs;
|
||||
}
|
||||
elsif ($ours ne '') {
|
||||
push @tracked, $theirs;
|
||||
}
|
||||
}
|
||||
}
|
||||
return \@new, \@stale, \@tracked;
|
||||
}
|
||||
|
||||
sub show_mapping {
|
||||
my ($name, $info) = @_;
|
||||
my ($new, $stale, $tracked) = list_mapping($name, $info);
|
||||
if (@$new) {
|
||||
print " New remote branches (next fetch will store in remotes/$name)\n";
|
||||
print " @$new\n";
|
||||
}
|
||||
if (@$stale) {
|
||||
print " Stale tracking branches in remotes/$name (use 'git remote prune')\n";
|
||||
print " @$stale\n";
|
||||
}
|
||||
if (@$tracked) {
|
||||
print " Tracked remote branches\n";
|
||||
print " @$tracked\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub prune_remote {
|
||||
my ($name, $ls_remote) = @_;
|
||||
if (!exists $remote->{$name}) {
|
||||
print STDERR "No such remote $name\n";
|
||||
return 1;
|
||||
}
|
||||
my $info = $remote->{$name};
|
||||
update_ls_remote($ls_remote, $info);
|
||||
|
||||
my ($new, $stale, $tracked) = list_mapping($name, $info);
|
||||
my $prefix = "refs/remotes/$name";
|
||||
foreach my $to_prune (@$stale) {
|
||||
my @v = $git->command(qw(rev-parse --verify), "$prefix/$to_prune");
|
||||
$git->command(qw(update-ref -d), "$prefix/$to_prune", $v[0]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub show_remote {
|
||||
my ($name, $ls_remote) = @_;
|
||||
if (!exists $remote->{$name}) {
|
||||
print STDERR "No such remote $name\n";
|
||||
return 1;
|
||||
}
|
||||
my $info = $remote->{$name};
|
||||
update_ls_remote($ls_remote, $info);
|
||||
|
||||
print "* remote $name\n";
|
||||
print " URL: $info->{'URL'}\n";
|
||||
for my $branchname (sort keys %$branch) {
|
||||
next unless (defined $branch->{$branchname}{'REMOTE'} &&
|
||||
$branch->{$branchname}{'REMOTE'} eq $name);
|
||||
my @merged = map {
|
||||
s|^refs/heads/||;
|
||||
$_;
|
||||
} split(' ',"@{$branch->{$branchname}{'MERGE'}}");
|
||||
next unless (@merged);
|
||||
print " Remote branch(es) merged with 'git pull' while on branch $branchname\n";
|
||||
print " @merged\n";
|
||||
}
|
||||
if ($info->{'LS_REMOTE'}) {
|
||||
show_mapping($name, $info);
|
||||
}
|
||||
if ($info->{'PUSH'}) {
|
||||
my @pushed = map {
|
||||
s|^refs/heads/||;
|
||||
s|^\+refs/heads/|+|;
|
||||
s|:refs/heads/|:|;
|
||||
$_;
|
||||
} @{$info->{'PUSH'}};
|
||||
print " Local branch(es) pushed with 'git push'\n";
|
||||
print " @pushed\n";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub add_remote {
|
||||
my ($name, $url, $opts) = @_;
|
||||
if (exists $remote->{$name}) {
|
||||
print STDERR "remote $name already exists.\n";
|
||||
exit(1);
|
||||
}
|
||||
$git->command('config', "remote.$name.url", $url);
|
||||
my $track = $opts->{'track'} || ["*"];
|
||||
|
||||
for (@$track) {
|
||||
$git->command('config', '--add', "remote.$name.fetch",
|
||||
$opts->{'mirror'} ?
|
||||
"+refs/$_:refs/$_" :
|
||||
"+refs/heads/$_:refs/remotes/$name/$_");
|
||||
}
|
||||
if ($opts->{'fetch'}) {
|
||||
$git->command('fetch', $name);
|
||||
}
|
||||
if (exists $opts->{'master'}) {
|
||||
$git->command('symbolic-ref', "refs/remotes/$name/HEAD",
|
||||
"refs/remotes/$name/$opts->{'master'}");
|
||||
}
|
||||
}
|
||||
|
||||
sub update_remote {
|
||||
my ($name) = @_;
|
||||
my @remotes;
|
||||
|
||||
my $conf = $git->config("remotes." . $name);
|
||||
if (defined($conf)) {
|
||||
@remotes = split(' ', $conf);
|
||||
} elsif ($name eq 'default') {
|
||||
@remotes = ();
|
||||
for (sort keys %$remote) {
|
||||
my $do_fetch = $git->config_bool("remote." . $_ .
|
||||
".skipDefaultUpdate");
|
||||
unless ($do_fetch) {
|
||||
push @remotes, $_;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print STDERR "Remote group $name does not exist.\n";
|
||||
exit(1);
|
||||
}
|
||||
for (@remotes) {
|
||||
print "Updating $_\n";
|
||||
$git->command('fetch', "$_");
|
||||
}
|
||||
}
|
||||
|
||||
sub rm_remote {
|
||||
my ($name) = @_;
|
||||
if (!exists $remote->{$name}) {
|
||||
print STDERR "No such remote $name\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
$git->command('config', '--remove-section', "remote.$name");
|
||||
|
||||
eval {
|
||||
my @trackers = $git->command('config', '--get-regexp',
|
||||
'branch.*.remote', $name);
|
||||
for (@trackers) {
|
||||
/^branch\.(.*)?\.remote/;
|
||||
$git->config('--unset', "branch.$1.remote");
|
||||
$git->config('--unset', "branch.$1.merge");
|
||||
}
|
||||
};
|
||||
|
||||
my @refs = $git->command('for-each-ref',
|
||||
'--format=%(refname) %(objectname)', "refs/remotes/$name");
|
||||
for (@refs) {
|
||||
my ($ref, $object) = split;
|
||||
$git->command(qw(update-ref -d), $ref, $object);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub add_usage {
|
||||
print STDERR "usage: git remote add [-f] [-t track]* [-m master] <name> <url>\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
my $VERBOSE = 0;
|
||||
@ARGV = grep {
|
||||
if ($_ eq '-v' or $_ eq '--verbose') {
|
||||
$VERBOSE=1;
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
} @ARGV;
|
||||
|
||||
if (!@ARGV) {
|
||||
for (sort keys %$remote) {
|
||||
print "$_";
|
||||
print "\t$remote->{$_}->{URL}" if $VERBOSE;
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
elsif ($ARGV[0] eq 'show') {
|
||||
my $ls_remote = 1;
|
||||
my $i;
|
||||
for ($i = 1; $i < @ARGV; $i++) {
|
||||
if ($ARGV[$i] eq '-n') {
|
||||
$ls_remote = 0;
|
||||
}
|
||||
else {
|
||||
last;
|
||||
}
|
||||
}
|
||||
if ($i >= @ARGV) {
|
||||
print STDERR "usage: git remote show <remote>\n";
|
||||
exit(1);
|
||||
}
|
||||
my $status = 0;
|
||||
for (; $i < @ARGV; $i++) {
|
||||
$status |= show_remote($ARGV[$i], $ls_remote);
|
||||
}
|
||||
exit($status);
|
||||
}
|
||||
elsif ($ARGV[0] eq 'update') {
|
||||
if (@ARGV <= 1) {
|
||||
update_remote("default");
|
||||
exit(1);
|
||||
}
|
||||
for (my $i = 1; $i < @ARGV; $i++) {
|
||||
update_remote($ARGV[$i]);
|
||||
}
|
||||
}
|
||||
elsif ($ARGV[0] eq 'prune') {
|
||||
my $ls_remote = 1;
|
||||
my $i;
|
||||
for ($i = 1; $i < @ARGV; $i++) {
|
||||
if ($ARGV[$i] eq '-n') {
|
||||
$ls_remote = 0;
|
||||
}
|
||||
else {
|
||||
last;
|
||||
}
|
||||
}
|
||||
if ($i >= @ARGV) {
|
||||
print STDERR "usage: git remote prune <remote>\n";
|
||||
exit(1);
|
||||
}
|
||||
my $status = 0;
|
||||
for (; $i < @ARGV; $i++) {
|
||||
$status |= prune_remote($ARGV[$i], $ls_remote);
|
||||
}
|
||||
exit($status);
|
||||
}
|
||||
elsif ($ARGV[0] eq 'add') {
|
||||
my %opts = ();
|
||||
while (1 < @ARGV && $ARGV[1] =~ /^-/) {
|
||||
my $opt = $ARGV[1];
|
||||
shift @ARGV;
|
||||
if ($opt eq '-f' || $opt eq '--fetch') {
|
||||
$opts{'fetch'} = 1;
|
||||
next;
|
||||
}
|
||||
if ($opt eq '-t' || $opt eq '--track') {
|
||||
if (@ARGV < 1) {
|
||||
add_usage();
|
||||
}
|
||||
$opts{'track'} ||= [];
|
||||
push @{$opts{'track'}}, $ARGV[1];
|
||||
shift @ARGV;
|
||||
next;
|
||||
}
|
||||
if ($opt eq '-m' || $opt eq '--master') {
|
||||
if ((@ARGV < 1) || exists $opts{'master'}) {
|
||||
add_usage();
|
||||
}
|
||||
$opts{'master'} = $ARGV[1];
|
||||
shift @ARGV;
|
||||
next;
|
||||
}
|
||||
if ($opt eq '--mirror') {
|
||||
$opts{'mirror'} = 1;
|
||||
next;
|
||||
}
|
||||
add_usage();
|
||||
}
|
||||
if (@ARGV != 3) {
|
||||
add_usage();
|
||||
}
|
||||
add_remote($ARGV[1], $ARGV[2], \%opts);
|
||||
}
|
||||
elsif ($ARGV[0] eq 'rm') {
|
||||
if (@ARGV <= 1) {
|
||||
print STDERR "usage: git remote rm <remote>\n";
|
||||
exit(1);
|
||||
}
|
||||
exit(rm_remote($ARGV[1]));
|
||||
}
|
||||
else {
|
||||
print STDERR "usage: git remote\n";
|
||||
print STDERR " git remote add <name> <url>\n";
|
||||
print STDERR " git remote rm <name>\n";
|
||||
print STDERR " git remote show <name>\n";
|
||||
print STDERR " git remote prune <name>\n";
|
||||
print STDERR " git remote update [group]\n";
|
||||
exit(1);
|
||||
}
|
|
@ -1,194 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Linus Torvalds
|
||||
#
|
||||
|
||||
OPTIONS_KEEPDASHDASH=
|
||||
OPTIONS_SPEC="\
|
||||
git repack [options]
|
||||
--
|
||||
a pack everything in a single pack
|
||||
A same as -a, and turn unreachable objects loose
|
||||
d remove redundant packs, and run git-prune-packed
|
||||
f pass --no-reuse-delta to git-pack-objects
|
||||
F pass --no-reuse-object to git-pack-objects
|
||||
n do not run git-update-server-info
|
||||
q,quiet be quiet
|
||||
l pass --local to git-pack-objects
|
||||
unpack-unreachable= with -A, do not loosen objects older than this
|
||||
Packing constraints
|
||||
window= size of the window used for delta compression
|
||||
window-memory= same as the above, but limit memory size instead of entries count
|
||||
depth= limits the maximum delta depth
|
||||
max-pack-size= maximum size of each packfile
|
||||
"
|
||||
SUBDIRECTORY_OK='Yes'
|
||||
. git-sh-setup
|
||||
|
||||
no_update_info= all_into_one= remove_redundant= unpack_unreachable=
|
||||
local= no_reuse= extra=
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-n) no_update_info=t ;;
|
||||
-a) all_into_one=t ;;
|
||||
-A) all_into_one=t
|
||||
unpack_unreachable=--unpack-unreachable ;;
|
||||
--unpack-unreachable)
|
||||
unpack_unreachable="--unpack-unreachable=$2"; shift ;;
|
||||
-d) remove_redundant=t ;;
|
||||
-q) GIT_QUIET=t ;;
|
||||
-f) no_reuse=--no-reuse-delta ;;
|
||||
-F) no_reuse=--no-reuse-object ;;
|
||||
-l) local=--local ;;
|
||||
--max-pack-size|--window|--window-memory|--depth)
|
||||
extra="$extra $1=$2"; shift ;;
|
||||
--) shift; break;;
|
||||
*) usage ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
case "$(git config --bool repack.usedeltabaseoffset || echo true)" in
|
||||
true)
|
||||
extra="$extra --delta-base-offset" ;;
|
||||
esac
|
||||
|
||||
PACKDIR="$GIT_OBJECT_DIRECTORY/pack"
|
||||
PACKTMP="$PACKDIR/.tmp-$$-pack"
|
||||
rm -f "$PACKTMP"-*
|
||||
trap 'rm -f "$PACKTMP"-*' 0 1 2 3 15
|
||||
|
||||
# There will be more repacking strategies to come...
|
||||
case ",$all_into_one," in
|
||||
,,)
|
||||
args='--unpacked --incremental'
|
||||
;;
|
||||
,t,)
|
||||
args= existing=
|
||||
if [ -d "$PACKDIR" ]; then
|
||||
for e in $(cd "$PACKDIR" && find . -type f -name '*.pack' \
|
||||
| sed -e 's/^\.\///' -e 's/\.pack$//')
|
||||
do
|
||||
if [ -e "$PACKDIR/$e.keep" ]; then
|
||||
: keep
|
||||
else
|
||||
existing="$existing $e"
|
||||
fi
|
||||
done
|
||||
if test -n "$existing" && test -n "$unpack_unreachable" && \
|
||||
test -n "$remove_redundant"
|
||||
then
|
||||
# This may have arbitrary user arguments, so we
|
||||
# have to protect it against whitespace splitting
|
||||
# when it gets run as "pack-objects $args" later.
|
||||
# Fortunately, we know it's an approxidate, so we
|
||||
# can just use dots instead.
|
||||
args="$args $(echo "$unpack_unreachable" | tr ' ' .)"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p "$PACKDIR" || exit
|
||||
|
||||
args="$args $local ${GIT_QUIET:+-q} $no_reuse$extra"
|
||||
names=$(git pack-objects --keep-true-parents --honor-pack-keep --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
|
||||
exit 1
|
||||
if [ -z "$names" ]; then
|
||||
say Nothing new to pack.
|
||||
fi
|
||||
|
||||
# Ok we have prepared all new packfiles.
|
||||
|
||||
# First see if there are packs of the same name and if so
|
||||
# if we can move them out of the way (this can happen if we
|
||||
# repacked immediately after packing fully.
|
||||
rollback=
|
||||
failed=
|
||||
for name in $names
|
||||
do
|
||||
for sfx in pack idx
|
||||
do
|
||||
file=pack-$name.$sfx
|
||||
test -f "$PACKDIR/$file" || continue
|
||||
rm -f "$PACKDIR/old-$file" &&
|
||||
mv "$PACKDIR/$file" "$PACKDIR/old-$file" || {
|
||||
failed=t
|
||||
break
|
||||
}
|
||||
rollback="$rollback $file"
|
||||
done
|
||||
test -z "$failed" || break
|
||||
done
|
||||
|
||||
# If renaming failed for any of them, roll the ones we have
|
||||
# already renamed back to their original names.
|
||||
if test -n "$failed"
|
||||
then
|
||||
rollback_failure=
|
||||
for file in $rollback
|
||||
do
|
||||
mv "$PACKDIR/old-$file" "$PACKDIR/$file" ||
|
||||
rollback_failure="$rollback_failure $file"
|
||||
done
|
||||
if test -n "$rollback_failure"
|
||||
then
|
||||
echo >&2 "WARNING: Some packs in use have been renamed by"
|
||||
echo >&2 "WARNING: prefixing old- to their name, in order to"
|
||||
echo >&2 "WARNING: replace them with the new version of the"
|
||||
echo >&2 "WARNING: file. But the operation failed, and"
|
||||
echo >&2 "WARNING: attempt to rename them back to their"
|
||||
echo >&2 "WARNING: original names also failed."
|
||||
echo >&2 "WARNING: Please rename them in $PACKDIR manually:"
|
||||
for file in $rollback_failure
|
||||
do
|
||||
echo >&2 "WARNING: old-$file -> $file"
|
||||
done
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Now the ones with the same name are out of the way...
|
||||
fullbases=
|
||||
for name in $names
|
||||
do
|
||||
fullbases="$fullbases pack-$name"
|
||||
chmod a-w "$PACKTMP-$name.pack"
|
||||
chmod a-w "$PACKTMP-$name.idx"
|
||||
mv -f "$PACKTMP-$name.pack" "$PACKDIR/pack-$name.pack" &&
|
||||
mv -f "$PACKTMP-$name.idx" "$PACKDIR/pack-$name.idx" ||
|
||||
exit
|
||||
done
|
||||
|
||||
# Remove the "old-" files
|
||||
for name in $names
|
||||
do
|
||||
rm -f "$PACKDIR/old-pack-$name.idx"
|
||||
rm -f "$PACKDIR/old-pack-$name.pack"
|
||||
done
|
||||
|
||||
# End of pack replacement.
|
||||
|
||||
if test "$remove_redundant" = t
|
||||
then
|
||||
# We know $existing are all redundant.
|
||||
if [ -n "$existing" ]
|
||||
then
|
||||
( cd "$PACKDIR" &&
|
||||
for e in $existing
|
||||
do
|
||||
case " $fullbases " in
|
||||
*" $e "*) ;;
|
||||
*) rm -f "$e.pack" "$e.idx" "$e.keep" ;;
|
||||
esac
|
||||
done
|
||||
)
|
||||
fi
|
||||
git prune-packed ${GIT_QUIET:+-q}
|
||||
fi
|
||||
|
||||
case "$no_update_info" in
|
||||
t) : ;;
|
||||
*) git update-server-info ;;
|
||||
esac
|
|
@ -1,284 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
#
|
||||
# REuse REcorded REsolve. This tool records a conflicted automerge
|
||||
# result and its hand resolution, and helps to resolve future
|
||||
# automerge that results in the same conflict.
|
||||
#
|
||||
# To enable this feature, create a directory 'rr-cache' under your
|
||||
# .git/ directory.
|
||||
|
||||
use Digest;
|
||||
use File::Path;
|
||||
use File::Copy;
|
||||
|
||||
my $git_dir = $::ENV{GIT_DIR} || ".git";
|
||||
my $rr_dir = "$git_dir/rr-cache";
|
||||
my $merge_rr = "$git_dir/rr-cache/MERGE_RR";
|
||||
|
||||
my %merge_rr = ();
|
||||
|
||||
sub read_rr {
|
||||
if (!-f $merge_rr) {
|
||||
%merge_rr = ();
|
||||
return;
|
||||
}
|
||||
my $in;
|
||||
local $/ = "\0";
|
||||
open $in, "<$merge_rr" or die "$!: $merge_rr";
|
||||
while (<$in>) {
|
||||
chomp;
|
||||
my ($name, $path) = /^([0-9a-f]{40})\t(.*)$/s;
|
||||
$merge_rr{$path} = $name;
|
||||
}
|
||||
close $in;
|
||||
}
|
||||
|
||||
sub write_rr {
|
||||
my $out;
|
||||
open $out, ">$merge_rr" or die "$!: $merge_rr";
|
||||
for my $path (sort keys %merge_rr) {
|
||||
my $name = $merge_rr{$path};
|
||||
print $out "$name\t$path\0";
|
||||
}
|
||||
close $out;
|
||||
}
|
||||
|
||||
sub compute_conflict_name {
|
||||
my ($path) = @_;
|
||||
my @side = ();
|
||||
my $in;
|
||||
open $in, "<$path" or die "$!: $path";
|
||||
|
||||
my $sha1 = Digest->new("SHA-1");
|
||||
my $hunk = 0;
|
||||
while (<$in>) {
|
||||
if (/^<<<<<<< .*/) {
|
||||
$hunk++;
|
||||
@side = ([], undef);
|
||||
}
|
||||
elsif (/^=======$/) {
|
||||
$side[1] = [];
|
||||
}
|
||||
elsif (/^>>>>>>> .*/) {
|
||||
my ($one, $two);
|
||||
$one = join('', @{$side[0]});
|
||||
$two = join('', @{$side[1]});
|
||||
if ($two le $one) {
|
||||
($one, $two) = ($two, $one);
|
||||
}
|
||||
$sha1->add($one);
|
||||
$sha1->add("\0");
|
||||
$sha1->add($two);
|
||||
$sha1->add("\0");
|
||||
@side = ();
|
||||
}
|
||||
elsif (@side == 0) {
|
||||
next;
|
||||
}
|
||||
elsif (defined $side[1]) {
|
||||
push @{$side[1]}, $_;
|
||||
}
|
||||
else {
|
||||
push @{$side[0]}, $_;
|
||||
}
|
||||
}
|
||||
close $in;
|
||||
return ($sha1->hexdigest, $hunk);
|
||||
}
|
||||
|
||||
sub record_preimage {
|
||||
my ($path, $name) = @_;
|
||||
my @side = ();
|
||||
my ($in, $out);
|
||||
open $in, "<$path" or die "$!: $path";
|
||||
open $out, ">$name" or die "$!: $name";
|
||||
|
||||
while (<$in>) {
|
||||
if (/^<<<<<<< .*/) {
|
||||
@side = ([], undef);
|
||||
}
|
||||
elsif (/^=======$/) {
|
||||
$side[1] = [];
|
||||
}
|
||||
elsif (/^>>>>>>> .*/) {
|
||||
my ($one, $two);
|
||||
$one = join('', @{$side[0]});
|
||||
$two = join('', @{$side[1]});
|
||||
if ($two le $one) {
|
||||
($one, $two) = ($two, $one);
|
||||
}
|
||||
print $out "<<<<<<<\n";
|
||||
print $out $one;
|
||||
print $out "=======\n";
|
||||
print $out $two;
|
||||
print $out ">>>>>>>\n";
|
||||
@side = ();
|
||||
}
|
||||
elsif (@side == 0) {
|
||||
print $out $_;
|
||||
}
|
||||
elsif (defined $side[1]) {
|
||||
push @{$side[1]}, $_;
|
||||
}
|
||||
else {
|
||||
push @{$side[0]}, $_;
|
||||
}
|
||||
}
|
||||
close $out;
|
||||
close $in;
|
||||
}
|
||||
|
||||
sub find_conflict {
|
||||
my $in;
|
||||
local $/ = "\0";
|
||||
my $pid = open($in, '-|');
|
||||
die "$!" unless defined $pid;
|
||||
if (!$pid) {
|
||||
exec(qw(git ls-files -z -u)) or die "$!: ls-files";
|
||||
}
|
||||
my %path = ();
|
||||
my @path = ();
|
||||
while (<$in>) {
|
||||
chomp;
|
||||
my ($mode, $sha1, $stage, $path) =
|
||||
/^([0-7]+) ([0-9a-f]{40}) ([123])\t(.*)$/s;
|
||||
$path{$path} |= (1 << $stage);
|
||||
}
|
||||
close $in;
|
||||
while (my ($path, $status) = each %path) {
|
||||
if ($status == 14) { push @path, $path; }
|
||||
}
|
||||
return @path;
|
||||
}
|
||||
|
||||
sub merge {
|
||||
my ($name, $path) = @_;
|
||||
record_preimage($path, "$rr_dir/$name/thisimage");
|
||||
unless (system('git', 'merge-file', map { "$rr_dir/$name/${_}image" }
|
||||
qw(this pre post))) {
|
||||
my $in;
|
||||
open $in, "<$rr_dir/$name/thisimage" or
|
||||
die "$!: $name/thisimage";
|
||||
my $out;
|
||||
open $out, ">$path" or die "$!: $path";
|
||||
while (<$in>) { print $out $_; }
|
||||
close $in;
|
||||
close $out;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub garbage_collect_rerere {
|
||||
# We should allow specifying these from the command line and
|
||||
# that is why the caller gives @ARGV to us, but I am lazy.
|
||||
|
||||
my $cutoff_noresolve = 15; # two weeks
|
||||
my $cutoff_resolve = 60; # two months
|
||||
my @to_remove;
|
||||
while (<$rr_dir/*/preimage>) {
|
||||
my ($dir) = /^(.*)\/preimage$/;
|
||||
my $cutoff = ((-f "$dir/postimage")
|
||||
? $cutoff_resolve
|
||||
: $cutoff_noresolve);
|
||||
my $age = -M "$_";
|
||||
if ($cutoff <= $age) {
|
||||
push @to_remove, $dir;
|
||||
}
|
||||
}
|
||||
if (@to_remove) {
|
||||
rmtree(\@to_remove);
|
||||
}
|
||||
}
|
||||
|
||||
-d "$rr_dir" || exit(0);
|
||||
|
||||
read_rr();
|
||||
|
||||
if (@ARGV) {
|
||||
my $arg = shift @ARGV;
|
||||
if ($arg eq 'clear') {
|
||||
for my $path (keys %merge_rr) {
|
||||
my $name = $merge_rr{$path};
|
||||
if (-d "$rr_dir/$name" &&
|
||||
! -f "$rr_dir/$name/postimage") {
|
||||
rmtree(["$rr_dir/$name"]);
|
||||
}
|
||||
}
|
||||
unlink $merge_rr;
|
||||
}
|
||||
elsif ($arg eq 'status') {
|
||||
for my $path (keys %merge_rr) {
|
||||
print $path, "\n";
|
||||
}
|
||||
}
|
||||
elsif ($arg eq 'diff') {
|
||||
for my $path (keys %merge_rr) {
|
||||
my $name = $merge_rr{$path};
|
||||
system('diff', ((@ARGV == 0) ? ('-u') : @ARGV),
|
||||
'-L', "a/$path", '-L', "b/$path",
|
||||
"$rr_dir/$name/preimage", $path);
|
||||
}
|
||||
}
|
||||
elsif ($arg eq 'gc') {
|
||||
garbage_collect_rerere(@ARGV);
|
||||
}
|
||||
else {
|
||||
die "$0 unknown command: $arg\n";
|
||||
}
|
||||
exit 0;
|
||||
}
|
||||
|
||||
my %conflict = map { $_ => 1 } find_conflict();
|
||||
|
||||
# MERGE_RR records paths with conflicts immediately after merge
|
||||
# failed. Some of the conflicted paths might have been hand resolved
|
||||
# in the working tree since then, but the initial run would catch all
|
||||
# and register their preimages.
|
||||
|
||||
for my $path (keys %conflict) {
|
||||
# This path has conflict. If it is not recorded yet,
|
||||
# record the pre-image.
|
||||
if (!exists $merge_rr{$path}) {
|
||||
my ($name, $hunk) = compute_conflict_name($path);
|
||||
next unless ($hunk);
|
||||
$merge_rr{$path} = $name;
|
||||
if (! -d "$rr_dir/$name") {
|
||||
mkpath("$rr_dir/$name", 0, 0777);
|
||||
print STDERR "Recorded preimage for '$path'\n";
|
||||
record_preimage($path, "$rr_dir/$name/preimage");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Now some of the paths that had conflicts earlier might have been
|
||||
# hand resolved. Others may be similar to a conflict already that
|
||||
# was resolved before.
|
||||
|
||||
for my $path (keys %merge_rr) {
|
||||
my $name = $merge_rr{$path};
|
||||
|
||||
# We could resolve this automatically if we have images.
|
||||
if (-f "$rr_dir/$name/preimage" &&
|
||||
-f "$rr_dir/$name/postimage") {
|
||||
if (merge($name, $path)) {
|
||||
print STDERR "Resolved '$path' using previous resolution.\n";
|
||||
# Then we do not have to worry about this path
|
||||
# anymore.
|
||||
delete $merge_rr{$path};
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
# Let's see if we have resolved it.
|
||||
(undef, my $hunk) = compute_conflict_name($path);
|
||||
next if ($hunk);
|
||||
|
||||
print STDERR "Recorded resolution for '$path'.\n";
|
||||
copy($path, "$rr_dir/$name/postimage");
|
||||
# And we do not have to worry about this path anymore.
|
||||
delete $merge_rr{$path};
|
||||
}
|
||||
|
||||
# Write out the rest.
|
||||
write_rr();
|
|
@ -1,106 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
|
||||
#
|
||||
USAGE='[--mixed | --soft | --hard] [<commit-ish>] [ [--] <paths>...]'
|
||||
SUBDIRECTORY_OK=Yes
|
||||
. git-sh-setup
|
||||
set_reflog_action "reset $*"
|
||||
require_work_tree
|
||||
|
||||
update= reset_type=--mixed
|
||||
unset rev
|
||||
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
--mixed | --soft | --hard)
|
||||
reset_type="$1"
|
||||
;;
|
||||
--)
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
rev=$(git rev-parse --verify "$1") || exit
|
||||
shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
: ${rev=HEAD}
|
||||
rev=$(git rev-parse --verify $rev^0) || exit
|
||||
|
||||
# Skip -- in "git reset HEAD -- foo" and "git reset -- foo".
|
||||
case "$1" in --) shift ;; esac
|
||||
|
||||
# git reset --mixed tree [--] paths... can be used to
|
||||
# load chosen paths from the tree into the index without
|
||||
# affecting the working tree or HEAD.
|
||||
if test $# != 0
|
||||
then
|
||||
test "$reset_type" = "--mixed" ||
|
||||
die "Cannot do partial $reset_type reset."
|
||||
|
||||
git diff-index --cached $rev -- "$@" |
|
||||
sed -e 's/^:\([0-7][0-7]*\) [0-7][0-7]* \([0-9a-f][0-9a-f]*\) [0-9a-f][0-9a-f]* [A-Z] \(.*\)$/\1 \2 \3/' |
|
||||
git update-index --add --remove --index-info || exit
|
||||
git update-index --refresh
|
||||
exit
|
||||
fi
|
||||
|
||||
cd_to_toplevel
|
||||
|
||||
if test "$reset_type" = "--hard"
|
||||
then
|
||||
update=-u
|
||||
fi
|
||||
|
||||
# Soft reset does not touch the index file or the working tree
|
||||
# at all, but requires them in a good order. Other resets reset
|
||||
# the index file to the tree object we are switching to.
|
||||
if test "$reset_type" = "--soft"
|
||||
then
|
||||
if test -f "$GIT_DIR/MERGE_HEAD" ||
|
||||
test "" != "$(git ls-files --unmerged)"
|
||||
then
|
||||
die "Cannot do a soft reset in the middle of a merge."
|
||||
fi
|
||||
else
|
||||
git read-tree -v --reset $update "$rev" || exit
|
||||
fi
|
||||
|
||||
# Any resets update HEAD to the head being switched to.
|
||||
if orig=$(git rev-parse --verify HEAD 2>/dev/null)
|
||||
then
|
||||
echo "$orig" >"$GIT_DIR/ORIG_HEAD"
|
||||
else
|
||||
rm -f "$GIT_DIR/ORIG_HEAD"
|
||||
fi
|
||||
git update-ref -m "$GIT_REFLOG_ACTION" HEAD "$rev"
|
||||
update_ref_status=$?
|
||||
|
||||
case "$reset_type" in
|
||||
--hard )
|
||||
test $update_ref_status = 0 && {
|
||||
printf "HEAD is now at "
|
||||
GIT_PAGER= git log --max-count=1 --pretty=oneline \
|
||||
--abbrev-commit HEAD
|
||||
}
|
||||
;;
|
||||
--soft )
|
||||
;; # Nothing else to do
|
||||
--mixed )
|
||||
# Report what has not been updated.
|
||||
git update-index --refresh
|
||||
;;
|
||||
esac
|
||||
|
||||
rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" \
|
||||
"$GIT_DIR/SQUASH_MSG" "$GIT_DIR/MERGE_MSG"
|
||||
|
||||
exit $update_ref_status
|
|
@ -1,112 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Linus Torvalds
|
||||
#
|
||||
# Resolve two trees.
|
||||
#
|
||||
|
||||
echo 'WARNING: This command is DEPRECATED and will be removed very soon.' >&2
|
||||
echo 'WARNING: Please use git-merge or git-pull instead.' >&2
|
||||
sleep 2
|
||||
|
||||
USAGE='<head> <remote> <merge-message>'
|
||||
. git-sh-setup
|
||||
|
||||
dropheads() {
|
||||
rm -f -- "$GIT_DIR/MERGE_HEAD" \
|
||||
"$GIT_DIR/LAST_MERGE" || exit 1
|
||||
}
|
||||
|
||||
head=$(git rev-parse --verify "$1"^0) &&
|
||||
merge=$(git rev-parse --verify "$2"^0) &&
|
||||
merge_name="$2" &&
|
||||
merge_msg="$3" || usage
|
||||
|
||||
#
|
||||
# The remote name is just used for the message,
|
||||
# but we do want it.
|
||||
#
|
||||
if [ -z "$head" -o -z "$merge" -o -z "$merge_msg" ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
dropheads
|
||||
echo $head > "$GIT_DIR"/ORIG_HEAD
|
||||
echo $merge > "$GIT_DIR"/LAST_MERGE
|
||||
|
||||
common=$(git merge-base $head $merge)
|
||||
if [ -z "$common" ]; then
|
||||
die "Unable to find common commit between" $merge $head
|
||||
fi
|
||||
|
||||
case "$common" in
|
||||
"$merge")
|
||||
echo "Already up to date. Yeeah!"
|
||||
dropheads
|
||||
exit 0
|
||||
;;
|
||||
"$head")
|
||||
echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $merge)"
|
||||
git read-tree -u -m $head $merge || exit 1
|
||||
git update-ref -m "resolve $merge_name: Fast-forward" \
|
||||
HEAD "$merge" "$head"
|
||||
git diff-tree -p $head $merge | git apply --stat
|
||||
dropheads
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# We are going to make a new commit.
|
||||
git var GIT_COMMITTER_IDENT >/dev/null || exit
|
||||
|
||||
# Find an optimum merge base if there are more than one candidates.
|
||||
LF='
|
||||
'
|
||||
common=$(git merge-base -a $head $merge)
|
||||
case "$common" in
|
||||
?*"$LF"?*)
|
||||
echo "Trying to find the optimum merge base."
|
||||
G=.tmp-index$$
|
||||
best=
|
||||
best_cnt=-1
|
||||
for c in $common
|
||||
do
|
||||
rm -f $G
|
||||
GIT_INDEX_FILE=$G git read-tree -m $c $head $merge \
|
||||
2>/dev/null || continue
|
||||
# Count the paths that are unmerged.
|
||||
cnt=$(GIT_INDEX_FILE=$G git ls-files --unmerged | wc -l)
|
||||
if test $best_cnt -le 0 || test $cnt -le $best_cnt
|
||||
then
|
||||
best=$c
|
||||
best_cnt=$cnt
|
||||
if test "$best_cnt" -eq 0
|
||||
then
|
||||
# Cannot do any better than all trivial merge.
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
rm -f $G
|
||||
common="$best"
|
||||
esac
|
||||
|
||||
echo "Trying to merge $merge into $head using $common."
|
||||
git update-index --refresh 2>/dev/null
|
||||
git read-tree -u -m $common $head $merge || exit 1
|
||||
result_tree=$(git write-tree 2> /dev/null)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Simple merge failed, trying Automatic merge"
|
||||
git-merge-index -o git-merge-one-file -a
|
||||
if [ $? -ne 0 ]; then
|
||||
echo $merge > "$GIT_DIR"/MERGE_HEAD
|
||||
die "Automatic merge failed, fix up by hand"
|
||||
fi
|
||||
result_tree=$(git write-tree) || exit 1
|
||||
fi
|
||||
result_commit=$(echo "$merge_msg" | git commit-tree $result_tree -p $head -p $merge)
|
||||
echo "Committed merge $result_commit"
|
||||
git update-ref -m "resolve $merge_name: In-index merge" \
|
||||
HEAD "$result_commit" "$head"
|
||||
git diff-tree -p $head $result_commit | git apply --stat
|
||||
dropheads
|
|
@ -1,207 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Linus Torvalds
|
||||
# Copyright (c) 2005 Junio C Hamano
|
||||
#
|
||||
|
||||
case "$0" in
|
||||
*-revert* )
|
||||
test -t 0 && edit=-e
|
||||
replay=
|
||||
me=revert
|
||||
USAGE='[--edit | --no-edit] [-n] <commit-ish>' ;;
|
||||
*-cherry-pick* )
|
||||
replay=t
|
||||
edit=
|
||||
me=cherry-pick
|
||||
USAGE='[--edit] [-n] [-r] [-x] <commit-ish>' ;;
|
||||
* )
|
||||
echo >&2 "What are you talking about?"
|
||||
exit 1 ;;
|
||||
esac
|
||||
|
||||
SUBDIRECTORY_OK=Yes ;# we will cd up
|
||||
. git-sh-setup
|
||||
require_work_tree
|
||||
cd_to_toplevel
|
||||
|
||||
no_commit=
|
||||
xopt=
|
||||
while case "$#" in 0) break ;; esac
|
||||
do
|
||||
case "$1" in
|
||||
-n|--n|--no|--no-|--no-c|--no-co|--no-com|--no-comm|\
|
||||
--no-commi|--no-commit)
|
||||
no_commit=t
|
||||
;;
|
||||
-e|--e|--ed|--edi|--edit)
|
||||
edit=-e
|
||||
;;
|
||||
--n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit)
|
||||
edit=
|
||||
;;
|
||||
-r)
|
||||
: no-op ;;
|
||||
-x|--i-really-want-to-expose-my-private-commit-object-name)
|
||||
replay=
|
||||
;;
|
||||
-X?*)
|
||||
xopt="$xopt$(git rev-parse --sq-quote "--${1#-X}")"
|
||||
;;
|
||||
--strategy-option=*)
|
||||
xopt="$xopt$(git rev-parse --sq-quote "--${1#--strategy-option=}")"
|
||||
;;
|
||||
-X|--strategy-option)
|
||||
shift
|
||||
xopt="$xopt$(git rev-parse --sq-quote "--$1")"
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
set_reflog_action "$me"
|
||||
|
||||
test "$me,$replay" = "revert,t" && usage
|
||||
|
||||
case "$no_commit" in
|
||||
t)
|
||||
# We do not intend to commit immediately. We just want to
|
||||
# merge the differences in.
|
||||
head=$(git-write-tree) ||
|
||||
die "Your index file is unmerged."
|
||||
;;
|
||||
*)
|
||||
head=$(git-rev-parse --verify HEAD) ||
|
||||
die "You do not have a valid HEAD"
|
||||
files=$(git-diff-index --cached --name-only $head) || exit
|
||||
if [ "$files" ]; then
|
||||
die "Dirty index: cannot $me (dirty: $files)"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
rev=$(git-rev-parse --verify "$@") &&
|
||||
commit=$(git-rev-parse --verify "$rev^0") ||
|
||||
die "Not a single commit $@"
|
||||
prev=$(git-rev-parse --verify "$commit^1" 2>/dev/null) ||
|
||||
die "Cannot run $me a root commit"
|
||||
git-rev-parse --verify "$commit^2" >/dev/null 2>&1 &&
|
||||
die "Cannot run $me a multi-parent commit."
|
||||
|
||||
encoding=$(git config i18n.commitencoding || echo UTF-8)
|
||||
|
||||
# "commit" is an existing commit. We would want to apply
|
||||
# the difference it introduces since its first parent "prev"
|
||||
# on top of the current HEAD if we are cherry-pick. Or the
|
||||
# reverse of it if we are revert.
|
||||
|
||||
case "$me" in
|
||||
revert)
|
||||
git show -s --pretty=oneline --encoding="$encoding" $commit |
|
||||
sed -e '
|
||||
s/^[^ ]* /Revert "/
|
||||
s/$/"/
|
||||
'
|
||||
echo
|
||||
echo "This reverts commit $commit."
|
||||
test "$rev" = "$commit" ||
|
||||
echo "(original 'git revert' arguments: $@)"
|
||||
base=$commit next=$prev
|
||||
;;
|
||||
|
||||
cherry-pick)
|
||||
pick_author_script='
|
||||
/^author /{
|
||||
s/'\''/'\''\\'\'\''/g
|
||||
h
|
||||
s/^author \([^<]*\) <[^>]*> .*$/\1/
|
||||
s/'\''/'\''\'\'\''/g
|
||||
s/.*/GIT_AUTHOR_NAME='\''&'\''/p
|
||||
|
||||
g
|
||||
s/^author [^<]* <\([^>]*\)> .*$/\1/
|
||||
s/'\''/'\''\'\'\''/g
|
||||
s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
|
||||
|
||||
g
|
||||
s/^author [^<]* <[^>]*> \(.*\)$/\1/
|
||||
s/'\''/'\''\'\'\''/g
|
||||
s/.*/GIT_AUTHOR_DATE='\''&'\''/p
|
||||
|
||||
q
|
||||
}'
|
||||
|
||||
logmsg=$(git show -s --pretty=raw --encoding="$encoding" "$commit")
|
||||
set_author_env=$(echo "$logmsg" |
|
||||
LANG=C LC_ALL=C sed -ne "$pick_author_script")
|
||||
eval "$set_author_env"
|
||||
export GIT_AUTHOR_NAME
|
||||
export GIT_AUTHOR_EMAIL
|
||||
export GIT_AUTHOR_DATE
|
||||
|
||||
echo "$logmsg" |
|
||||
sed -e '1,/^$/d' -e 's/^ //'
|
||||
case "$replay" in
|
||||
'')
|
||||
echo "(cherry picked from commit $commit)"
|
||||
test "$rev" = "$commit" ||
|
||||
echo "(original 'git cherry-pick' arguments: $@)"
|
||||
;;
|
||||
esac
|
||||
base=$prev next=$commit
|
||||
;;
|
||||
|
||||
esac >.msg
|
||||
|
||||
eval GITHEAD_$head=HEAD
|
||||
eval GITHEAD_$next='$(git show -s \
|
||||
--pretty=oneline --encoding="$encoding" "$commit" |
|
||||
sed -e "s/^[^ ]* //")'
|
||||
export GITHEAD_$head GITHEAD_$next
|
||||
|
||||
# This three way merge is an interesting one. We are at
|
||||
# $head, and would want to apply the change between $commit
|
||||
# and $prev on top of us (when reverting), or the change between
|
||||
# $prev and $commit on top of us (when cherry-picking or replaying).
|
||||
|
||||
eval "git merge-recursive $xopt $base -- $head $next" &&
|
||||
result=$(git-write-tree 2>/dev/null) || {
|
||||
mv -f .msg "$GIT_DIR/MERGE_MSG"
|
||||
{
|
||||
echo '
|
||||
Conflicts:
|
||||
'
|
||||
git ls-files --unmerged |
|
||||
sed -e 's/^[^ ]* / /' |
|
||||
uniq
|
||||
} >>"$GIT_DIR/MERGE_MSG"
|
||||
echo >&2 "Automatic $me failed. After resolving the conflicts,"
|
||||
echo >&2 "mark the corrected paths with 'git-add <paths>'"
|
||||
echo >&2 "and commit the result."
|
||||
case "$me" in
|
||||
cherry-pick)
|
||||
echo >&2 "You may choose to use the following when making"
|
||||
echo >&2 "the commit:"
|
||||
echo >&2 "$set_author_env"
|
||||
esac
|
||||
exit 1
|
||||
}
|
||||
|
||||
# If we are cherry-pick, and if the merge did not result in
|
||||
# hand-editing, we will hit this commit and inherit the original
|
||||
# author date and name.
|
||||
# If we are revert, or if our cherry-pick results in a hand merge,
|
||||
# we had better say that the current user is responsible for that.
|
||||
|
||||
case "$no_commit" in
|
||||
'')
|
||||
git-commit -n -F .msg $edit
|
||||
rm -f .msg
|
||||
;;
|
||||
esac
|
|
@ -1,976 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
# This tool is copyright (c) 2005, Matthias Urlichs.
|
||||
# It is released under the Gnu Public License, version 2.
|
||||
#
|
||||
# The basic idea is to pull and analyze SVN changes.
|
||||
#
|
||||
# Checking out the files is done by a single long-running SVN connection.
|
||||
#
|
||||
# The head revision is on branch "origin" by default.
|
||||
# You can change that with the '-o' option.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Getopt::Std;
|
||||
use File::Copy;
|
||||
use File::Spec;
|
||||
use File::Temp qw(tempfile);
|
||||
use File::Path qw(mkpath);
|
||||
use File::Basename qw(basename dirname);
|
||||
use Time::Local;
|
||||
use IO::Pipe;
|
||||
use POSIX qw(strftime dup2);
|
||||
use IPC::Open2;
|
||||
use SVN::Core;
|
||||
use SVN::Ra;
|
||||
|
||||
die "Need SVN:Core 1.2.1 or better" if $SVN::Core::VERSION lt "1.2.1";
|
||||
|
||||
$SIG{'PIPE'}="IGNORE";
|
||||
$ENV{'TZ'}="UTC";
|
||||
|
||||
our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T,
|
||||
$opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S,$opt_F,
|
||||
$opt_P,$opt_R);
|
||||
|
||||
sub usage() {
|
||||
print STDERR <<END;
|
||||
usage: ${\basename $0} # fetch/update GIT from SVN
|
||||
[-o branch-for-HEAD] [-h] [-v] [-l max_rev] [-R repack_each_revs]
|
||||
[-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
|
||||
[-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg]
|
||||
[-m] [-M regex] [-A author_file] [-S] [-F] [-P project_name] [SVN_URL]
|
||||
END
|
||||
exit(1);
|
||||
}
|
||||
|
||||
getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:SP:R:uv") or usage();
|
||||
usage if $opt_h;
|
||||
|
||||
my $tag_name = $opt_t || "tags";
|
||||
my $trunk_name = defined $opt_T ? $opt_T : "trunk";
|
||||
my $branch_name = $opt_b || "branches";
|
||||
my $project_name = $opt_P || "";
|
||||
$project_name = "/" . $project_name if ($project_name);
|
||||
my $repack_after = $opt_R || 1000;
|
||||
my $root_pool = SVN::Pool->new_default;
|
||||
|
||||
@ARGV == 1 or @ARGV == 2 or usage();
|
||||
|
||||
$opt_o ||= "origin";
|
||||
$opt_s ||= 1;
|
||||
my $git_tree = $opt_C;
|
||||
$git_tree ||= ".";
|
||||
|
||||
my $svn_url = $ARGV[0];
|
||||
my $svn_dir = $ARGV[1];
|
||||
|
||||
our @mergerx = ();
|
||||
if ($opt_m) {
|
||||
my $branch_esc = quotemeta ($branch_name);
|
||||
my $trunk_esc = quotemeta ($trunk_name);
|
||||
@mergerx =
|
||||
(
|
||||
qr!\b(?:merg(?:ed?|ing))\b.*?\b((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i,
|
||||
qr!\b(?:from|of)\W+((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i,
|
||||
qr!\b(?:from|of)\W+(?:the )?([\w\.\-]+)[-\s]branch\b!i
|
||||
);
|
||||
}
|
||||
if ($opt_M) {
|
||||
unshift (@mergerx, qr/$opt_M/);
|
||||
}
|
||||
|
||||
# Absolutize filename now, since we will have chdir'ed by the time we
|
||||
# get around to opening it.
|
||||
$opt_A = File::Spec->rel2abs($opt_A) if $opt_A;
|
||||
|
||||
our %users = ();
|
||||
our $users_file = undef;
|
||||
sub read_users($) {
|
||||
$users_file = File::Spec->rel2abs(@_);
|
||||
die "Cannot open $users_file\n" unless -f $users_file;
|
||||
open(my $authors,$users_file);
|
||||
while(<$authors>) {
|
||||
chomp;
|
||||
next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/;
|
||||
(my $user,my $name,my $email) = ($1,$2,$3);
|
||||
$users{$user} = [$name,$email];
|
||||
}
|
||||
close($authors);
|
||||
}
|
||||
|
||||
select(STDERR); $|=1; select(STDOUT);
|
||||
|
||||
|
||||
package SVNconn;
|
||||
# Basic SVN connection.
|
||||
# We're only interested in connecting and downloading, so ...
|
||||
|
||||
use File::Spec;
|
||||
use File::Temp qw(tempfile);
|
||||
use POSIX qw(strftime dup2);
|
||||
use Fcntl qw(SEEK_SET);
|
||||
|
||||
sub new {
|
||||
my($what,$repo) = @_;
|
||||
$what=ref($what) if ref($what);
|
||||
|
||||
my $self = {};
|
||||
$self->{'buffer'} = "";
|
||||
bless($self,$what);
|
||||
|
||||
$repo =~ s#/+$##;
|
||||
$self->{'fullrep'} = $repo;
|
||||
$self->conn();
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub conn {
|
||||
my $self = shift;
|
||||
my $repo = $self->{'fullrep'};
|
||||
my $auth = SVN::Core::auth_open ([SVN::Client::get_simple_provider,
|
||||
SVN::Client::get_ssl_server_trust_file_provider,
|
||||
SVN::Client::get_username_provider]);
|
||||
my $s = SVN::Ra->new(url => $repo, auth => $auth, pool => $root_pool);
|
||||
die "SVN connection to $repo: $!\n" unless defined $s;
|
||||
$self->{'svn'} = $s;
|
||||
$self->{'repo'} = $repo;
|
||||
$self->{'maxrev'} = $s->get_latest_revnum();
|
||||
}
|
||||
|
||||
sub file {
|
||||
my($self,$path,$rev) = @_;
|
||||
|
||||
my ($fh, $name) = tempfile('gitsvn.XXXXXX',
|
||||
DIR => File::Spec->tmpdir(), UNLINK => 1);
|
||||
|
||||
print "... $rev $path ...\n" if $opt_v;
|
||||
my (undef, $properties);
|
||||
$path =~ s#^/*##;
|
||||
my $subpool = SVN::Pool::new_default_sub;
|
||||
eval { (undef, $properties)
|
||||
= $self->{'svn'}->get_file($path,$rev,$fh); };
|
||||
if($@) {
|
||||
return undef if $@ =~ /Attempted to get checksum/;
|
||||
die $@;
|
||||
}
|
||||
my $mode;
|
||||
if (exists $properties->{'svn:executable'}) {
|
||||
$mode = '100755';
|
||||
} elsif (exists $properties->{'svn:special'}) {
|
||||
my ($special_content, $filesize);
|
||||
$filesize = tell $fh;
|
||||
seek $fh, 0, SEEK_SET;
|
||||
read $fh, $special_content, $filesize;
|
||||
if ($special_content =~ s/^link //) {
|
||||
$mode = '120000';
|
||||
seek $fh, 0, SEEK_SET;
|
||||
truncate $fh, 0;
|
||||
print $fh $special_content;
|
||||
} else {
|
||||
die "unexpected svn:special file encountered";
|
||||
}
|
||||
} else {
|
||||
$mode = '100644';
|
||||
}
|
||||
close ($fh);
|
||||
|
||||
return ($name, $mode);
|
||||
}
|
||||
|
||||
sub ignore {
|
||||
my($self,$path,$rev) = @_;
|
||||
|
||||
print "... $rev $path ...\n" if $opt_v;
|
||||
$path =~ s#^/*##;
|
||||
my $subpool = SVN::Pool::new_default_sub;
|
||||
my (undef,undef,$properties)
|
||||
= $self->{'svn'}->get_dir($path,$rev,undef);
|
||||
if (exists $properties->{'svn:ignore'}) {
|
||||
my ($fh, $name) = tempfile('gitsvn.XXXXXX',
|
||||
DIR => File::Spec->tmpdir(),
|
||||
UNLINK => 1);
|
||||
print $fh $properties->{'svn:ignore'};
|
||||
close($fh);
|
||||
return $name;
|
||||
} else {
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
sub dir_list {
|
||||
my($self,$path,$rev) = @_;
|
||||
$path =~ s#^/*##;
|
||||
my $subpool = SVN::Pool::new_default_sub;
|
||||
my ($dirents,undef,$properties)
|
||||
= $self->{'svn'}->get_dir($path,$rev,undef);
|
||||
return $dirents;
|
||||
}
|
||||
|
||||
package main;
|
||||
use URI;
|
||||
|
||||
our $svn = $svn_url;
|
||||
$svn .= "/$svn_dir" if defined $svn_dir;
|
||||
my $svn2 = SVNconn->new($svn);
|
||||
$svn = SVNconn->new($svn);
|
||||
|
||||
my $lwp_ua;
|
||||
if($opt_d or $opt_D) {
|
||||
$svn_url = URI->new($svn_url)->canonical;
|
||||
if($opt_D) {
|
||||
$svn_dir =~ s#/*$#/#;
|
||||
} else {
|
||||
$svn_dir = "";
|
||||
}
|
||||
if ($svn_url->scheme eq "http") {
|
||||
use LWP::UserAgent;
|
||||
$lwp_ua = LWP::UserAgent->new(keep_alive => 1, requests_redirectable => []);
|
||||
} else {
|
||||
print STDERR "Warning: not HTTP; turning off direct file access\n";
|
||||
$opt_d=0;
|
||||
}
|
||||
}
|
||||
|
||||
sub pdate($) {
|
||||
my($d) = @_;
|
||||
$d =~ m#(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)#
|
||||
or die "Unparseable date: $d\n";
|
||||
my $y=$1; $y+=1900 if $y<1000;
|
||||
return timegm($6||0,$5,$4,$3,$2-1,$y);
|
||||
}
|
||||
|
||||
sub getwd() {
|
||||
my $pwd = `pwd`;
|
||||
chomp $pwd;
|
||||
return $pwd;
|
||||
}
|
||||
|
||||
|
||||
sub get_headref($$) {
|
||||
my $name = shift;
|
||||
my $git_dir = shift;
|
||||
my $sha;
|
||||
|
||||
if (open(C,"$git_dir/refs/heads/$name")) {
|
||||
chomp($sha = <C>);
|
||||
close(C);
|
||||
length($sha) == 40
|
||||
or die "Cannot get head id for $name ($sha): $!\n";
|
||||
}
|
||||
return $sha;
|
||||
}
|
||||
|
||||
|
||||
-d $git_tree
|
||||
or mkdir($git_tree,0777)
|
||||
or die "Could not create $git_tree: $!";
|
||||
chdir($git_tree);
|
||||
|
||||
my $orig_branch = "";
|
||||
my $forward_master = 0;
|
||||
my %branches;
|
||||
|
||||
my $git_dir = $ENV{"GIT_DIR"} || ".git";
|
||||
$git_dir = getwd()."/".$git_dir unless $git_dir =~ m#^/#;
|
||||
$ENV{"GIT_DIR"} = $git_dir;
|
||||
my $orig_git_index;
|
||||
$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};
|
||||
my ($git_ih, $git_index) = tempfile('gitXXXXXX', SUFFIX => '.idx',
|
||||
DIR => File::Spec->tmpdir());
|
||||
close ($git_ih);
|
||||
$ENV{GIT_INDEX_FILE} = $git_index;
|
||||
my $maxnum = 0;
|
||||
my $last_rev = "";
|
||||
my $last_branch;
|
||||
my $current_rev = $opt_s || 1;
|
||||
unless(-d $git_dir) {
|
||||
system("git init");
|
||||
die "Cannot init the GIT db at $git_tree: $?\n" if $?;
|
||||
system("git read-tree --empty");
|
||||
die "Cannot init an empty tree: $?\n" if $?;
|
||||
|
||||
$last_branch = $opt_o;
|
||||
$orig_branch = "";
|
||||
} else {
|
||||
-f "$git_dir/refs/heads/$opt_o"
|
||||
or die "Branch '$opt_o' does not exist.\n".
|
||||
"Either use the correct '-o branch' option,\n".
|
||||
"or import to a new repository.\n";
|
||||
|
||||
-f "$git_dir/svn2git"
|
||||
or die "'$git_dir/svn2git' does not exist.\n".
|
||||
"You need that file for incremental imports.\n";
|
||||
open(F, "git symbolic-ref HEAD |") or
|
||||
die "Cannot run git-symbolic-ref: $!\n";
|
||||
chomp ($last_branch = <F>);
|
||||
$last_branch = basename($last_branch);
|
||||
close(F);
|
||||
unless($last_branch) {
|
||||
warn "Cannot read the last branch name: $! -- assuming 'master'\n";
|
||||
$last_branch = "master";
|
||||
}
|
||||
$orig_branch = $last_branch;
|
||||
$last_rev = get_headref($orig_branch, $git_dir);
|
||||
if (-f "$git_dir/SVN2GIT_HEAD") {
|
||||
die <<EOM;
|
||||
SVN2GIT_HEAD exists.
|
||||
Make sure your working directory corresponds to HEAD and remove SVN2GIT_HEAD.
|
||||
You may need to run
|
||||
|
||||
git-read-tree -m -u SVN2GIT_HEAD HEAD
|
||||
EOM
|
||||
}
|
||||
system('cp', "$git_dir/HEAD", "$git_dir/SVN2GIT_HEAD");
|
||||
|
||||
$forward_master =
|
||||
$opt_o ne 'master' && -f "$git_dir/refs/heads/master" &&
|
||||
system('cmp', '-s', "$git_dir/refs/heads/master",
|
||||
"$git_dir/refs/heads/$opt_o") == 0;
|
||||
|
||||
# populate index
|
||||
system('git', 'read-tree', $last_rev);
|
||||
die "read-tree failed: $?\n" if $?;
|
||||
|
||||
# Get the last import timestamps
|
||||
open my $B,"<", "$git_dir/svn2git";
|
||||
while(<$B>) {
|
||||
chomp;
|
||||
my($num,$branch,$ref) = split;
|
||||
$branches{$branch}{$num} = $ref;
|
||||
$branches{$branch}{"LAST"} = $ref;
|
||||
$current_rev = $num+1 if $current_rev <= $num;
|
||||
}
|
||||
close($B);
|
||||
}
|
||||
-d $git_dir
|
||||
or die "Could not create git subdir ($git_dir).\n";
|
||||
|
||||
my $default_authors = "$git_dir/svn-authors";
|
||||
if ($opt_A) {
|
||||
read_users($opt_A);
|
||||
copy($opt_A,$default_authors) or die "Copy failed: $!";
|
||||
} else {
|
||||
read_users($default_authors) if -f $default_authors;
|
||||
}
|
||||
|
||||
open BRANCHES,">>", "$git_dir/svn2git";
|
||||
|
||||
sub node_kind($$) {
|
||||
my ($svnpath, $revision) = @_;
|
||||
$svnpath =~ s#^/*##;
|
||||
my $subpool = SVN::Pool::new_default_sub;
|
||||
my $kind = $svn->{'svn'}->check_path($svnpath,$revision);
|
||||
return $kind;
|
||||
}
|
||||
|
||||
sub get_file($$$) {
|
||||
my($svnpath,$rev,$path) = @_;
|
||||
|
||||
# now get it
|
||||
my ($name,$mode);
|
||||
if($opt_d) {
|
||||
my($req,$res);
|
||||
|
||||
# /svn/!svn/bc/2/django/trunk/django-docs/build.py
|
||||
my $url=$svn_url->clone();
|
||||
$url->path($url->path."/!svn/bc/$rev/$svn_dir$svnpath");
|
||||
print "... $path...\n" if $opt_v;
|
||||
$req = HTTP::Request->new(GET => $url);
|
||||
$res = $lwp_ua->request($req);
|
||||
if ($res->is_success) {
|
||||
my $fh;
|
||||
($fh, $name) = tempfile('gitsvn.XXXXXX',
|
||||
DIR => File::Spec->tmpdir(), UNLINK => 1);
|
||||
print $fh $res->content;
|
||||
close($fh) or die "Could not write $name: $!\n";
|
||||
} else {
|
||||
return undef if $res->code == 301; # directory?
|
||||
die $res->status_line." at $url\n";
|
||||
}
|
||||
$mode = '0644'; # can't obtain mode via direct http request?
|
||||
} else {
|
||||
($name,$mode) = $svn->file("$svnpath",$rev);
|
||||
return undef unless defined $name;
|
||||
}
|
||||
|
||||
my $pid = open(my $F, '-|');
|
||||
die $! unless defined $pid;
|
||||
if (!$pid) {
|
||||
exec("git", "hash-object", "-w", $name)
|
||||
or die "Cannot create object: $!\n";
|
||||
}
|
||||
my $sha = <$F>;
|
||||
chomp $sha;
|
||||
close $F;
|
||||
unlink $name;
|
||||
return [$mode, $sha, $path];
|
||||
}
|
||||
|
||||
sub get_ignore($$$$$) {
|
||||
my($new,$old,$rev,$path,$svnpath) = @_;
|
||||
|
||||
return unless $opt_I;
|
||||
my $name = $svn->ignore("$svnpath",$rev);
|
||||
if ($path eq '/') {
|
||||
$path = $opt_I;
|
||||
} else {
|
||||
$path = File::Spec->catfile($path,$opt_I);
|
||||
}
|
||||
if (defined $name) {
|
||||
my $pid = open(my $F, '-|');
|
||||
die $! unless defined $pid;
|
||||
if (!$pid) {
|
||||
exec("git", "hash-object", "-w", $name)
|
||||
or die "Cannot create object: $!\n";
|
||||
}
|
||||
my $sha = <$F>;
|
||||
chomp $sha;
|
||||
close $F;
|
||||
unlink $name;
|
||||
push(@$new,['0644',$sha,$path]);
|
||||
} elsif (defined $old) {
|
||||
push(@$old,$path);
|
||||
}
|
||||
}
|
||||
|
||||
sub project_path($$)
|
||||
{
|
||||
my ($path, $project) = @_;
|
||||
|
||||
$path = "/".$path unless ($path =~ m#^\/#) ;
|
||||
return $1 if ($path =~ m#^$project\/(.*)$#);
|
||||
|
||||
$path =~ s#\.#\\\.#g;
|
||||
$path =~ s#\+#\\\+#g;
|
||||
return "/" if ($project =~ m#^$path.*$#);
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub split_path($$) {
|
||||
my($rev,$path) = @_;
|
||||
my $branch;
|
||||
|
||||
if($path =~ s#^/\Q$tag_name\E/([^/]+)/?##) {
|
||||
$branch = "/$1";
|
||||
} elsif($path =~ s#^/\Q$trunk_name\E/?##) {
|
||||
$branch = "/";
|
||||
} elsif($path =~ s#^/\Q$branch_name\E/([^/]+)/?##) {
|
||||
$branch = $1;
|
||||
} else {
|
||||
my %no_error = (
|
||||
"/" => 1,
|
||||
"/$tag_name" => 1,
|
||||
"/$branch_name" => 1
|
||||
);
|
||||
print STDERR "$rev: Unrecognized path: $path\n" unless (defined $no_error{$path});
|
||||
return ()
|
||||
}
|
||||
if ($path eq "") {
|
||||
$path = "/";
|
||||
} elsif ($project_name) {
|
||||
$path = project_path($path, $project_name);
|
||||
}
|
||||
return ($branch,$path);
|
||||
}
|
||||
|
||||
sub branch_rev($$) {
|
||||
|
||||
my ($srcbranch,$uptorev) = @_;
|
||||
|
||||
my $bbranches = $branches{$srcbranch};
|
||||
my @revs = reverse sort { ($a eq 'LAST' ? 0 : $a) <=> ($b eq 'LAST' ? 0 : $b) } keys %$bbranches;
|
||||
my $therev;
|
||||
foreach my $arev(@revs) {
|
||||
next if ($arev eq 'LAST');
|
||||
if ($arev <= $uptorev) {
|
||||
$therev = $arev;
|
||||
last;
|
||||
}
|
||||
}
|
||||
return $therev;
|
||||
}
|
||||
|
||||
sub expand_svndir($$$);
|
||||
|
||||
sub expand_svndir($$$)
|
||||
{
|
||||
my ($svnpath, $rev, $path) = @_;
|
||||
my @list;
|
||||
get_ignore(\@list, undef, $rev, $path, $svnpath);
|
||||
my $dirents = $svn->dir_list($svnpath, $rev);
|
||||
foreach my $p(keys %$dirents) {
|
||||
my $kind = node_kind($svnpath.'/'.$p, $rev);
|
||||
if ($kind eq $SVN::Node::file) {
|
||||
my $f = get_file($svnpath.'/'.$p, $rev, $path.'/'.$p);
|
||||
push(@list, $f) if $f;
|
||||
} elsif ($kind eq $SVN::Node::dir) {
|
||||
push(@list,
|
||||
expand_svndir($svnpath.'/'.$p, $rev, $path.'/'.$p));
|
||||
}
|
||||
}
|
||||
return @list;
|
||||
}
|
||||
|
||||
sub copy_path($$$$$$$$) {
|
||||
# Somebody copied a whole subdirectory.
|
||||
# We need to find the index entries from the old version which the
|
||||
# SVN log entry points to, and add them to the new place.
|
||||
|
||||
my($newrev,$newbranch,$path,$oldpath,$rev,$node_kind,$new,$parents) = @_;
|
||||
|
||||
my($srcbranch,$srcpath) = split_path($rev,$oldpath);
|
||||
unless(defined $srcbranch && defined $srcpath) {
|
||||
print "Path not found when copying from $oldpath @ $rev.\n".
|
||||
"Will try to copy from original SVN location...\n"
|
||||
if $opt_v;
|
||||
push (@$new, expand_svndir($oldpath, $rev, $path));
|
||||
return;
|
||||
}
|
||||
my $therev = branch_rev($srcbranch, $rev);
|
||||
my $gitrev = $branches{$srcbranch}{$therev};
|
||||
unless($gitrev) {
|
||||
print STDERR "$newrev:$newbranch: could not find $oldpath \@ $rev\n";
|
||||
return;
|
||||
}
|
||||
if ($srcbranch ne $newbranch) {
|
||||
push(@$parents, $branches{$srcbranch}{'LAST'});
|
||||
}
|
||||
print "$newrev:$newbranch:$path: copying from $srcbranch:$srcpath @ $rev\n" if $opt_v;
|
||||
if ($node_kind eq $SVN::Node::dir) {
|
||||
$srcpath =~ s#/*$#/#;
|
||||
}
|
||||
|
||||
my $pid = open my $f,'-|';
|
||||
die $! unless defined $pid;
|
||||
if (!$pid) {
|
||||
exec("git","ls-tree","-r","-z",$gitrev,$srcpath)
|
||||
or die $!;
|
||||
}
|
||||
local $/ = "\0";
|
||||
while(<$f>) {
|
||||
chomp;
|
||||
my($m,$p) = split(/\t/,$_,2);
|
||||
my($mode,$type,$sha1) = split(/ /,$m);
|
||||
next if $type ne "blob";
|
||||
if ($node_kind eq $SVN::Node::dir) {
|
||||
$p = $path . substr($p,length($srcpath)-1);
|
||||
} else {
|
||||
$p = $path;
|
||||
}
|
||||
push(@$new,[$mode,$sha1,$p]);
|
||||
}
|
||||
close($f) or
|
||||
print STDERR "$newrev:$newbranch: could not list files in $oldpath \@ $rev\n";
|
||||
}
|
||||
|
||||
sub commit {
|
||||
my($branch, $changed_paths, $revision, $author, $date, $message) = @_;
|
||||
my($committer_name,$committer_email,$dest);
|
||||
my($author_name,$author_email);
|
||||
my(@old,@new,@parents);
|
||||
|
||||
if (not defined $author or $author eq "") {
|
||||
$committer_name = $committer_email = "unknown";
|
||||
} elsif (defined $users_file) {
|
||||
die "User $author is not listed in $users_file\n"
|
||||
unless exists $users{$author};
|
||||
($committer_name,$committer_email) = @{$users{$author}};
|
||||
} elsif ($author =~ /^(.*?)\s+<(.*)>$/) {
|
||||
($committer_name, $committer_email) = ($1, $2);
|
||||
} else {
|
||||
$author =~ s/^<(.*)>$/$1/;
|
||||
$committer_name = $committer_email = $author;
|
||||
}
|
||||
|
||||
if ($opt_F && $message =~ /From:\s+(.*?)\s+<(.*)>\s*\n/) {
|
||||
($author_name, $author_email) = ($1, $2);
|
||||
print "Author from From: $1 <$2>\n" if ($opt_v);;
|
||||
} elsif ($opt_S && $message =~ /Signed-off-by:\s+(.*?)\s+<(.*)>\s*\n/) {
|
||||
($author_name, $author_email) = ($1, $2);
|
||||
print "Author from Signed-off-by: $1 <$2>\n" if ($opt_v);;
|
||||
} else {
|
||||
$author_name = $committer_name;
|
||||
$author_email = $committer_email;
|
||||
}
|
||||
|
||||
$date = pdate($date);
|
||||
|
||||
my $tag;
|
||||
my $parent;
|
||||
if($branch eq "/") { # trunk
|
||||
$parent = $opt_o;
|
||||
} elsif($branch =~ m#^/(.+)#) { # tag
|
||||
$tag = 1;
|
||||
$parent = $1;
|
||||
} else { # "normal" branch
|
||||
# nothing to do
|
||||
$parent = $branch;
|
||||
}
|
||||
$dest = $parent;
|
||||
|
||||
my $prev = $changed_paths->{"/"};
|
||||
if($prev and $prev->[0] eq "A") {
|
||||
delete $changed_paths->{"/"};
|
||||
my $oldpath = $prev->[1];
|
||||
my $rev;
|
||||
if(defined $oldpath) {
|
||||
my $p;
|
||||
($parent,$p) = split_path($revision,$oldpath);
|
||||
if(defined $parent) {
|
||||
if($parent eq "/") {
|
||||
$parent = $opt_o;
|
||||
} else {
|
||||
$parent =~ s#^/##; # if it's a tag
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$parent = undef;
|
||||
}
|
||||
}
|
||||
|
||||
my $rev;
|
||||
if($revision > $opt_s and defined $parent) {
|
||||
open(H,'-|',"git","rev-parse","--verify",$parent);
|
||||
$rev = <H>;
|
||||
close(H) or do {
|
||||
print STDERR "$revision: cannot find commit '$parent'!\n";
|
||||
return;
|
||||
};
|
||||
chop $rev;
|
||||
if(length($rev) != 40) {
|
||||
print STDERR "$revision: cannot find commit '$parent'!\n";
|
||||
return;
|
||||
}
|
||||
$rev = $branches{($parent eq $opt_o) ? "/" : $parent}{"LAST"};
|
||||
if($revision != $opt_s and not $rev) {
|
||||
print STDERR "$revision: do not know ancestor for '$parent'!\n";
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$rev = undef;
|
||||
}
|
||||
|
||||
# if($prev and $prev->[0] eq "A") {
|
||||
# if(not $tag) {
|
||||
# unless(open(H,"> $git_dir/refs/heads/$branch")) {
|
||||
# print STDERR "$revision: Could not create branch $branch: $!\n";
|
||||
# $state=11;
|
||||
# next;
|
||||
# }
|
||||
# print H "$rev\n"
|
||||
# or die "Could not write branch $branch: $!";
|
||||
# close(H)
|
||||
# or die "Could not write branch $branch: $!";
|
||||
# }
|
||||
# }
|
||||
if(not defined $rev) {
|
||||
unlink($git_index);
|
||||
} elsif ($rev ne $last_rev) {
|
||||
print "Switching from $last_rev to $rev ($branch)\n" if $opt_v;
|
||||
system("git", "read-tree", $rev);
|
||||
die "read-tree failed for $rev: $?\n" if $?;
|
||||
$last_rev = $rev;
|
||||
}
|
||||
|
||||
push (@parents, $rev) if defined $rev;
|
||||
|
||||
my $cid;
|
||||
if($tag and not %$changed_paths) {
|
||||
$cid = $rev;
|
||||
} else {
|
||||
my @paths = sort keys %$changed_paths;
|
||||
foreach my $path(@paths) {
|
||||
my $action = $changed_paths->{$path};
|
||||
|
||||
if ($action->[0] eq "R") {
|
||||
# refer to a file/tree in an earlier commit
|
||||
push(@old,$path); # remove any old stuff
|
||||
}
|
||||
if(($action->[0] eq "A") || ($action->[0] eq "R")) {
|
||||
my $node_kind = node_kind($action->[3], $revision);
|
||||
if ($node_kind eq $SVN::Node::file) {
|
||||
my $f = get_file($action->[3],
|
||||
$revision, $path);
|
||||
if ($f) {
|
||||
push(@new,$f) if $f;
|
||||
} else {
|
||||
my $opath = $action->[3];
|
||||
print STDERR "$revision: $branch: could not fetch '$opath'\n";
|
||||
}
|
||||
} elsif ($node_kind eq $SVN::Node::dir) {
|
||||
if($action->[1]) {
|
||||
copy_path($revision, $branch,
|
||||
$path, $action->[1],
|
||||
$action->[2], $node_kind,
|
||||
\@new, \@parents);
|
||||
} else {
|
||||
get_ignore(\@new, \@old, $revision,
|
||||
$path, $action->[3]);
|
||||
}
|
||||
}
|
||||
} elsif ($action->[0] eq "D") {
|
||||
push(@old,$path);
|
||||
} elsif ($action->[0] eq "M") {
|
||||
my $node_kind = node_kind($action->[3], $revision);
|
||||
if ($node_kind eq $SVN::Node::file) {
|
||||
my $f = get_file($action->[3],
|
||||
$revision, $path);
|
||||
push(@new,$f) if $f;
|
||||
} elsif ($node_kind eq $SVN::Node::dir) {
|
||||
get_ignore(\@new, \@old, $revision,
|
||||
$path, $action->[3]);
|
||||
}
|
||||
} else {
|
||||
die "$revision: unknown action '".$action->[0]."' for $path\n";
|
||||
}
|
||||
}
|
||||
|
||||
while(@old) {
|
||||
my @o1;
|
||||
if(@old > 55) {
|
||||
@o1 = splice(@old,0,50);
|
||||
} else {
|
||||
@o1 = @old;
|
||||
@old = ();
|
||||
}
|
||||
my $pid = open my $F, "-|";
|
||||
die "$!" unless defined $pid;
|
||||
if (!$pid) {
|
||||
exec("git", "ls-files", "-z", @o1) or die $!;
|
||||
}
|
||||
@o1 = ();
|
||||
local $/ = "\0";
|
||||
while(<$F>) {
|
||||
chomp;
|
||||
push(@o1,$_);
|
||||
}
|
||||
close($F);
|
||||
|
||||
while(@o1) {
|
||||
my @o2;
|
||||
if(@o1 > 55) {
|
||||
@o2 = splice(@o1,0,50);
|
||||
} else {
|
||||
@o2 = @o1;
|
||||
@o1 = ();
|
||||
}
|
||||
system("git","update-index","--force-remove","--",@o2);
|
||||
die "Cannot remove files: $?\n" if $?;
|
||||
}
|
||||
}
|
||||
while(@new) {
|
||||
my @n2;
|
||||
if(@new > 12) {
|
||||
@n2 = splice(@new,0,10);
|
||||
} else {
|
||||
@n2 = @new;
|
||||
@new = ();
|
||||
}
|
||||
system("git","update-index","--add",
|
||||
(map { ('--cacheinfo', @$_) } @n2));
|
||||
die "Cannot add files: $?\n" if $?;
|
||||
}
|
||||
|
||||
my $pid = open(C,"-|");
|
||||
die "Cannot fork: $!" unless defined $pid;
|
||||
unless($pid) {
|
||||
exec("git","write-tree");
|
||||
die "Cannot exec git-write-tree: $!\n";
|
||||
}
|
||||
chomp(my $tree = <C>);
|
||||
length($tree) == 40
|
||||
or die "Cannot get tree id ($tree): $!\n";
|
||||
close(C)
|
||||
or die "Error running git-write-tree: $?\n";
|
||||
print "Tree ID $tree\n" if $opt_v;
|
||||
|
||||
my $pr = IO::Pipe->new() or die "Cannot open pipe: $!\n";
|
||||
my $pw = IO::Pipe->new() or die "Cannot open pipe: $!\n";
|
||||
$pid = fork();
|
||||
die "Fork: $!\n" unless defined $pid;
|
||||
unless($pid) {
|
||||
$pr->writer();
|
||||
$pw->reader();
|
||||
open(OUT,">&STDOUT");
|
||||
dup2($pw->fileno(),0);
|
||||
dup2($pr->fileno(),1);
|
||||
$pr->close();
|
||||
$pw->close();
|
||||
|
||||
my @par = ();
|
||||
|
||||
# loose detection of merges
|
||||
# based on the commit msg
|
||||
foreach my $rx (@mergerx) {
|
||||
if ($message =~ $rx) {
|
||||
my $mparent = $1;
|
||||
if ($mparent eq 'HEAD') { $mparent = $opt_o };
|
||||
if ( -e "$git_dir/refs/heads/$mparent") {
|
||||
$mparent = get_headref($mparent, $git_dir);
|
||||
push (@parents, $mparent);
|
||||
print OUT "Merge parent branch: $mparent\n" if $opt_v;
|
||||
}
|
||||
}
|
||||
}
|
||||
my %seen_parents = ();
|
||||
my @unique_parents = grep { ! $seen_parents{$_} ++ } @parents;
|
||||
foreach my $bparent (@unique_parents) {
|
||||
push @par, '-p', $bparent;
|
||||
print OUT "Merge parent branch: $bparent\n" if $opt_v;
|
||||
}
|
||||
|
||||
exec("env",
|
||||
"GIT_AUTHOR_NAME=$author_name",
|
||||
"GIT_AUTHOR_EMAIL=$author_email",
|
||||
"GIT_AUTHOR_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
|
||||
"GIT_COMMITTER_NAME=$committer_name",
|
||||
"GIT_COMMITTER_EMAIL=$committer_email",
|
||||
"GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)),
|
||||
"git", "commit-tree", $tree,@par);
|
||||
die "Cannot exec git-commit-tree: $!\n";
|
||||
}
|
||||
$pw->writer();
|
||||
$pr->reader();
|
||||
|
||||
$message =~ s/[\s\n]+\z//;
|
||||
$message = "r$revision: $message" if $opt_r;
|
||||
|
||||
print $pw "$message\n"
|
||||
or die "Error writing to git-commit-tree: $!\n";
|
||||
$pw->close();
|
||||
|
||||
print "Committed change $revision:$branch ".strftime("%Y-%m-%d %H:%M:%S",gmtime($date)).")\n" if $opt_v;
|
||||
chomp($cid = <$pr>);
|
||||
length($cid) == 40
|
||||
or die "Cannot get commit id ($cid): $!\n";
|
||||
print "Commit ID $cid\n" if $opt_v;
|
||||
$pr->close();
|
||||
|
||||
waitpid($pid,0);
|
||||
die "Error running git-commit-tree: $?\n" if $?;
|
||||
}
|
||||
|
||||
if (not defined $cid) {
|
||||
$cid = $branches{"/"}{"LAST"};
|
||||
}
|
||||
|
||||
if(not defined $dest) {
|
||||
print "... no known parent\n" if $opt_v;
|
||||
} elsif(not $tag) {
|
||||
print "Writing to refs/heads/$dest\n" if $opt_v;
|
||||
open(C,">$git_dir/refs/heads/$dest") and
|
||||
print C ("$cid\n") and
|
||||
close(C)
|
||||
or die "Cannot write branch $dest for update: $!\n";
|
||||
}
|
||||
|
||||
if ($tag) {
|
||||
$last_rev = "-" if %$changed_paths;
|
||||
# the tag was 'complex', i.e. did not refer to a "real" revision
|
||||
|
||||
$dest =~ tr/_/\./ if $opt_u;
|
||||
|
||||
system('git', 'tag', '-f', $dest, $cid) == 0
|
||||
or die "Cannot create tag $dest: $!\n";
|
||||
|
||||
print "Created tag '$dest' on '$branch'\n" if $opt_v;
|
||||
}
|
||||
$branches{$branch}{"LAST"} = $cid;
|
||||
$branches{$branch}{$revision} = $cid;
|
||||
$last_rev = $cid;
|
||||
print BRANCHES "$revision $branch $cid\n";
|
||||
print "DONE: $revision $dest $cid\n" if $opt_v;
|
||||
}
|
||||
|
||||
sub commit_all {
|
||||
# Recursive use of the SVN connection does not work
|
||||
local $svn = $svn2;
|
||||
|
||||
my ($changed_paths, $revision, $author, $date, $message) = @_;
|
||||
my %p;
|
||||
while(my($path,$action) = each %$changed_paths) {
|
||||
$p{$path} = [ $action->action,$action->copyfrom_path, $action->copyfrom_rev, $path ];
|
||||
}
|
||||
$changed_paths = \%p;
|
||||
|
||||
my %done;
|
||||
my @col;
|
||||
my $pref;
|
||||
my $branch;
|
||||
|
||||
while(my($path,$action) = each %$changed_paths) {
|
||||
($branch,$path) = split_path($revision,$path);
|
||||
next if not defined $branch;
|
||||
next if not defined $path;
|
||||
$done{$branch}{$path} = $action;
|
||||
}
|
||||
while(($branch,$changed_paths) = each %done) {
|
||||
commit($branch, $changed_paths, $revision, $author, $date, $message);
|
||||
}
|
||||
}
|
||||
|
||||
$opt_l = $svn->{'maxrev'} if not defined $opt_l or $opt_l > $svn->{'maxrev'};
|
||||
|
||||
if ($opt_l < $current_rev) {
|
||||
print "Up to date: no new revisions to fetch!\n" if $opt_v;
|
||||
unlink("$git_dir/SVN2GIT_HEAD");
|
||||
exit;
|
||||
}
|
||||
|
||||
print "Processing from $current_rev to $opt_l ...\n" if $opt_v;
|
||||
|
||||
my $from_rev;
|
||||
my $to_rev = $current_rev - 1;
|
||||
|
||||
my $subpool = SVN::Pool::new_default_sub;
|
||||
while ($to_rev < $opt_l) {
|
||||
$subpool->clear;
|
||||
$from_rev = $to_rev + 1;
|
||||
$to_rev = $from_rev + $repack_after;
|
||||
$to_rev = $opt_l if $opt_l < $to_rev;
|
||||
print "Fetching from $from_rev to $to_rev ...\n" if $opt_v;
|
||||
$svn->{'svn'}->get_log("",$from_rev,$to_rev,0,1,1,\&commit_all);
|
||||
my $pid = fork();
|
||||
die "Fork: $!\n" unless defined $pid;
|
||||
unless($pid) {
|
||||
exec("git", "repack", "-d")
|
||||
or die "Cannot repack: $!\n";
|
||||
}
|
||||
waitpid($pid, 0);
|
||||
}
|
||||
|
||||
|
||||
unlink($git_index);
|
||||
|
||||
if (defined $orig_git_index) {
|
||||
$ENV{GIT_INDEX_FILE} = $orig_git_index;
|
||||
} else {
|
||||
delete $ENV{GIT_INDEX_FILE};
|
||||
}
|
||||
|
||||
# Now switch back to the branch we were in before all of this happened
|
||||
if($orig_branch) {
|
||||
print "DONE\n" if $opt_v and (not defined $opt_l or $opt_l > 0);
|
||||
system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master")
|
||||
if $forward_master;
|
||||
unless ($opt_i) {
|
||||
system('git', 'read-tree', '-m', '-u', 'SVN2GIT_HEAD', 'HEAD');
|
||||
die "read-tree failed: $?\n" if $?;
|
||||
}
|
||||
} else {
|
||||
$orig_branch = "master";
|
||||
print "DONE; creating $orig_branch branch\n" if $opt_v and (not defined $opt_l or $opt_l > 0);
|
||||
system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master")
|
||||
unless -f "$git_dir/refs/heads/master";
|
||||
system('git', 'update-ref', 'HEAD', "$orig_branch");
|
||||
unless ($opt_i) {
|
||||
system('git checkout');
|
||||
die "checkout failed: $?\n" if $?;
|
||||
}
|
||||
}
|
||||
unlink("$git_dir/SVN2GIT_HEAD");
|
||||
close(BRANCHES);
|
|
@ -1,179 +0,0 @@
|
|||
git-svnimport(1)
|
||||
================
|
||||
v0.1, July 2005
|
||||
|
||||
NAME
|
||||
----
|
||||
git-svnimport - Import a SVN repository into git
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-svnimport' [ -o <branch-for-HEAD> ] [ -h ] [ -v ] [ -d | -D ]
|
||||
[ -C <GIT_repository> ] [ -i ] [ -u ] [-l limit_rev]
|
||||
[ -b branch_subdir ] [ -T trunk_subdir ] [ -t tag_subdir ]
|
||||
[ -s start_chg ] [ -m ] [ -r ] [ -M regex ]
|
||||
[ -I <ignorefile_name> ] [ -A <author_file> ]
|
||||
[ -R <repack_each_revs>] [ -P <path_from_trunk> ]
|
||||
<SVN_repository_URL> [ <path> ]
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Imports a SVN repository into git. It will either create a new
|
||||
repository, or incrementally import into an existing one.
|
||||
|
||||
SVN access is done by the SVN::Perl module.
|
||||
|
||||
git-svnimport assumes that SVN repositories are organized into one
|
||||
"trunk" directory where the main development happens, "branches/FOO"
|
||||
directories for branches, and "/tags/FOO" directories for tags.
|
||||
Other subdirectories are ignored.
|
||||
|
||||
git-svnimport creates a file ".git/svn2git", which is required for
|
||||
incremental SVN imports.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-C <target-dir>::
|
||||
The GIT repository to import to. If the directory doesn't
|
||||
exist, it will be created. Default is the current directory.
|
||||
|
||||
-s <start_rev>::
|
||||
Start importing at this SVN change number. The default is 1.
|
||||
+
|
||||
When importing incrementally, you might need to edit the .git/svn2git file.
|
||||
|
||||
-i::
|
||||
Import-only: don't perform a checkout after importing. This option
|
||||
ensures the working directory and index remain untouched and will
|
||||
not create them if they do not exist.
|
||||
|
||||
-T <trunk_subdir>::
|
||||
Name the SVN trunk. Default "trunk".
|
||||
|
||||
-t <tag_subdir>::
|
||||
Name the SVN subdirectory for tags. Default "tags".
|
||||
|
||||
-b <branch_subdir>::
|
||||
Name the SVN subdirectory for branches. Default "branches".
|
||||
|
||||
-o <branch-for-HEAD>::
|
||||
The 'trunk' branch from SVN is imported to the 'origin' branch within
|
||||
the git repository. Use this option if you want to import into a
|
||||
different branch.
|
||||
|
||||
-r::
|
||||
Prepend 'rX: ' to commit messages, where X is the imported
|
||||
subversion revision.
|
||||
|
||||
-u::
|
||||
Replace underscores in tag names with periods.
|
||||
|
||||
-I <ignorefile_name>::
|
||||
Import the svn:ignore directory property to files with this
|
||||
name in each directory. (The Subversion and GIT ignore
|
||||
syntaxes are similar enough that using the Subversion patterns
|
||||
directly with "-I .gitignore" will almost always just work.)
|
||||
|
||||
-A <author_file>::
|
||||
Read a file with lines on the form
|
||||
+
|
||||
------
|
||||
username = User's Full Name <email@addr.es>
|
||||
|
||||
------
|
||||
+
|
||||
and use "User's Full Name <email@addr.es>" as the GIT
|
||||
author and committer for Subversion commits made by
|
||||
"username". If encountering a commit made by a user not in the
|
||||
list, abort.
|
||||
+
|
||||
For convenience, this data is saved to $GIT_DIR/svn-authors
|
||||
each time the -A option is provided, and read from that same
|
||||
file each time git-svnimport is run with an existing GIT
|
||||
repository without -A.
|
||||
|
||||
-m::
|
||||
Attempt to detect merges based on the commit message. This option
|
||||
will enable default regexes that try to capture the name source
|
||||
branch name from the commit message.
|
||||
|
||||
-M <regex>::
|
||||
Attempt to detect merges based on the commit message with a custom
|
||||
regex. It can be used with -m to also see the default regexes.
|
||||
You must escape forward slashes.
|
||||
|
||||
-l <max_rev>::
|
||||
Specify a maximum revision number to pull.
|
||||
+
|
||||
Formerly, this option controlled how many revisions to pull,
|
||||
due to SVN memory leaks. (These have been worked around.)
|
||||
|
||||
-R <repack_each_revs>::
|
||||
Specify how often git repository should be repacked.
|
||||
+
|
||||
The default value is 1000. git-svnimport will do imports in chunks of 1000
|
||||
revisions, after each chunk the git repository will be repacked. To disable
|
||||
this behavior specify some large value here which is greater than the number of
|
||||
revisions to import.
|
||||
|
||||
-P <path_from_trunk>::
|
||||
Partial import of the SVN tree.
|
||||
+
|
||||
By default, the whole tree on the SVN trunk (/trunk) is imported.
|
||||
'-P my/proj' will import starting only from '/trunk/my/proj'.
|
||||
This option is useful when you want to import one project from a
|
||||
svn repo which hosts multiple projects under the same trunk.
|
||||
|
||||
-v::
|
||||
Verbosity: let 'svnimport' report what it is doing.
|
||||
|
||||
-d::
|
||||
Use direct HTTP requests if possible. The "<path>" argument is used
|
||||
only for retrieving the SVN logs; the path to the contents is
|
||||
included in the SVN log.
|
||||
|
||||
-D::
|
||||
Use direct HTTP requests if possible. The "<path>" argument is used
|
||||
for retrieving the logs, as well as for the contents.
|
||||
+
|
||||
There's no safe way to automatically find out which of these options to
|
||||
use, so you need to try both. Usually, the one that's wrong will die
|
||||
with a 40x error pretty quickly.
|
||||
|
||||
<SVN_repository_URL>::
|
||||
The URL of the SVN module you want to import. For local
|
||||
repositories, use "file:///absolute/path".
|
||||
+
|
||||
If you're using the "-d" or "-D" option, this is the URL of the SVN
|
||||
repository itself; it usually ends in "/svn".
|
||||
|
||||
<path>::
|
||||
The path to the module you want to check out.
|
||||
|
||||
-h::
|
||||
Print a short usage message and exit.
|
||||
|
||||
OUTPUT
|
||||
------
|
||||
If '-v' is specified, the script reports what it is doing.
|
||||
|
||||
Otherwise, success is indicated the Unix way, i.e. by simply exiting with
|
||||
a zero exit status.
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Matthias Urlichs <smurf@smurf.noris.de>, with help from
|
||||
various participants of the git-list <git@vger.kernel.org>.
|
||||
|
||||
Based on a cvs2git script by the same author.
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
Documentation by Matthias Urlichs <smurf@smurf.noris.de>.
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the linkgit:git[7] suite
|
|
@ -1,205 +0,0 @@
|
|||
#!/bin/sh
|
||||
# Copyright (c) 2005 Linus Torvalds
|
||||
|
||||
USAGE='[-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg>] <tagname> [<head>]'
|
||||
SUBDIRECTORY_OK='Yes'
|
||||
. git-sh-setup
|
||||
|
||||
message_given=
|
||||
annotate=
|
||||
signed=
|
||||
force=
|
||||
message=
|
||||
username=
|
||||
list=
|
||||
verify=
|
||||
LINES=0
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-a)
|
||||
annotate=1
|
||||
shift
|
||||
;;
|
||||
-s)
|
||||
annotate=1
|
||||
signed=1
|
||||
shift
|
||||
;;
|
||||
-f)
|
||||
force=1
|
||||
shift
|
||||
;;
|
||||
-n)
|
||||
case "$#,$2" in
|
||||
1,* | *,-*)
|
||||
LINES=1 # no argument
|
||||
;;
|
||||
*) shift
|
||||
LINES=$(expr "$1" : '\([0-9]*\)')
|
||||
[ -z "$LINES" ] && LINES=1 # 1 line is default when -n is used
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
;;
|
||||
-l)
|
||||
list=1
|
||||
shift
|
||||
case $# in
|
||||
0) PATTERN=
|
||||
;;
|
||||
*)
|
||||
PATTERN="$1" # select tags by shell pattern, not re
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
git rev-parse --symbolic --tags | sort |
|
||||
while read TAG
|
||||
do
|
||||
case "$TAG" in
|
||||
*$PATTERN*) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
[ "$LINES" -le 0 ] && { echo "$TAG"; continue ;}
|
||||
OBJTYPE=$(git cat-file -t "$TAG")
|
||||
case $OBJTYPE in
|
||||
tag)
|
||||
ANNOTATION=$(git cat-file tag "$TAG" |
|
||||
sed -e '1,/^$/d' |
|
||||
sed -n -e "
|
||||
/^-----BEGIN PGP SIGNATURE-----\$/q
|
||||
2,\$s/^/ /
|
||||
p
|
||||
${LINES}q
|
||||
")
|
||||
printf "%-15s %s\n" "$TAG" "$ANNOTATION"
|
||||
;;
|
||||
*) echo "$TAG"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
;;
|
||||
-m)
|
||||
annotate=1
|
||||
shift
|
||||
message="$1"
|
||||
if test "$#" = "0"; then
|
||||
die "error: option -m needs an argument"
|
||||
else
|
||||
message="$1"
|
||||
message_given=1
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
-F)
|
||||
annotate=1
|
||||
shift
|
||||
if test "$#" = "0"; then
|
||||
die "error: option -F needs an argument"
|
||||
else
|
||||
message="$(cat "$1")"
|
||||
message_given=1
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
-u)
|
||||
annotate=1
|
||||
signed=1
|
||||
shift
|
||||
if test "$#" = "0"; then
|
||||
die "error: option -u needs an argument"
|
||||
else
|
||||
username="$1"
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
-d)
|
||||
shift
|
||||
had_error=0
|
||||
for tag
|
||||
do
|
||||
cur=$(git show-ref --verify --hash -- "refs/tags/$tag") || {
|
||||
echo >&2 "Seriously, what tag are you talking about?"
|
||||
had_error=1
|
||||
continue
|
||||
}
|
||||
git update-ref -m 'tag: delete' -d "refs/tags/$tag" "$cur" || {
|
||||
had_error=1
|
||||
continue
|
||||
}
|
||||
echo "Deleted tag $tag."
|
||||
done
|
||||
exit $had_error
|
||||
;;
|
||||
-v)
|
||||
shift
|
||||
tag_name="$1"
|
||||
tag=$(git show-ref --verify --hash -- "refs/tags/$tag_name") ||
|
||||
die "Seriously, what tag are you talking about?"
|
||||
git-verify-tag -v "$tag"
|
||||
exit $?
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$list" ] && exit 0
|
||||
|
||||
name="$1"
|
||||
[ "$name" ] || usage
|
||||
prev=0000000000000000000000000000000000000000
|
||||
if git show-ref --verify --quiet -- "refs/tags/$name"
|
||||
then
|
||||
test -n "$force" || die "tag '$name' already exists"
|
||||
prev=$(git rev-parse "refs/tags/$name")
|
||||
fi
|
||||
shift
|
||||
git check-ref-format "tags/$name" ||
|
||||
die "we do not like '$name' as a tag name."
|
||||
|
||||
object=$(git rev-parse --verify --default HEAD "$@") || exit 1
|
||||
type=$(git cat-file -t $object) || exit 1
|
||||
tagger=$(git var GIT_COMMITTER_IDENT) || exit 1
|
||||
|
||||
test -n "$username" ||
|
||||
username=$(git config user.signingkey) ||
|
||||
username=$(expr "z$tagger" : 'z\(.*>\)')
|
||||
|
||||
trap 'rm -f "$GIT_DIR"/TAG_TMP* "$GIT_DIR"/TAG_FINALMSG "$GIT_DIR"/TAG_EDITMSG' 0
|
||||
|
||||
if [ "$annotate" ]; then
|
||||
if [ -z "$message_given" ]; then
|
||||
( echo "#"
|
||||
echo "# Write a tag message"
|
||||
echo "#" ) > "$GIT_DIR"/TAG_EDITMSG
|
||||
git_editor "$GIT_DIR"/TAG_EDITMSG || exit
|
||||
else
|
||||
printf '%s\n' "$message" >"$GIT_DIR"/TAG_EDITMSG
|
||||
fi
|
||||
|
||||
grep -v '^#' <"$GIT_DIR"/TAG_EDITMSG |
|
||||
git stripspace >"$GIT_DIR"/TAG_FINALMSG
|
||||
|
||||
[ -s "$GIT_DIR"/TAG_FINALMSG -o -n "$message_given" ] || {
|
||||
echo >&2 "No tag message?"
|
||||
exit 1
|
||||
}
|
||||
|
||||
( printf 'object %s\ntype %s\ntag %s\ntagger %s\n\n' \
|
||||
"$object" "$type" "$name" "$tagger";
|
||||
cat "$GIT_DIR"/TAG_FINALMSG ) >"$GIT_DIR"/TAG_TMP
|
||||
rm -f "$GIT_DIR"/TAG_TMP.asc "$GIT_DIR"/TAG_FINALMSG
|
||||
if [ "$signed" ]; then
|
||||
gpg -bsa -u "$username" "$GIT_DIR"/TAG_TMP &&
|
||||
cat "$GIT_DIR"/TAG_TMP.asc >>"$GIT_DIR"/TAG_TMP ||
|
||||
die "failed to sign the tag with GPG."
|
||||
fi
|
||||
object=$(git-mktag < "$GIT_DIR"/TAG_TMP)
|
||||
fi
|
||||
|
||||
git update-ref "refs/tags/$name" "$object" "$prev"
|
|
@ -1,45 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
USAGE='<tag>'
|
||||
SUBDIRECTORY_OK='Yes'
|
||||
. git-sh-setup
|
||||
|
||||
verbose=
|
||||
while test $# != 0
|
||||
do
|
||||
case "$1" in
|
||||
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
|
||||
verbose=t ;;
|
||||
*)
|
||||
break ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ "$#" != "1" ]
|
||||
then
|
||||
usage
|
||||
fi
|
||||
|
||||
type="$(git cat-file -t "$1" 2>/dev/null)" ||
|
||||
die "$1: no such object."
|
||||
|
||||
test "$type" = tag ||
|
||||
die "$1: cannot verify a non-tag object of type $type."
|
||||
|
||||
case "$verbose" in
|
||||
t)
|
||||
git cat-file -p "$1" |
|
||||
sed -n -e '/^-----BEGIN PGP SIGNATURE-----/q' -e p
|
||||
;;
|
||||
esac
|
||||
|
||||
trap 'rm -f "$GIT_DIR/.tmp-vtag"' 0
|
||||
|
||||
git cat-file tag "$1" >"$GIT_DIR/.tmp-vtag" || exit 1
|
||||
sed -n -e '
|
||||
/^-----BEGIN PGP SIGNATURE-----$/q
|
||||
p
|
||||
' <"$GIT_DIR/.tmp-vtag" |
|
||||
gpg --verify "$GIT_DIR/.tmp-vtag" - || exit 1
|
||||
rm -f "$GIT_DIR/.tmp-vtag"
|
|
@ -1,28 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
USAGE='[-p] [--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [-m] [git-diff-tree options] [git-rev-list options]'
|
||||
SUBDIRECTORY_OK='Yes'
|
||||
. git-sh-setup
|
||||
|
||||
diff_tree_flags=$(git-rev-parse --sq --no-revs --flags "$@") || exit
|
||||
case "$0" in
|
||||
*whatchanged)
|
||||
count=
|
||||
test -z "$diff_tree_flags" &&
|
||||
diff_tree_flags=$(git config --get whatchanged.difftree)
|
||||
diff_tree_default_flags='-c -M --abbrev' ;;
|
||||
*show)
|
||||
count=-n1
|
||||
test -z "$diff_tree_flags" &&
|
||||
diff_tree_flags=$(git config --get show.difftree)
|
||||
diff_tree_default_flags='--cc --always' ;;
|
||||
esac
|
||||
test -z "$diff_tree_flags" &&
|
||||
diff_tree_flags="$diff_tree_default_flags"
|
||||
|
||||
rev_list_args=$(git-rev-parse --sq --default HEAD --revs-only "$@") &&
|
||||
diff_tree_args=$(git-rev-parse --sq --no-revs --no-flags "$@") &&
|
||||
|
||||
eval "git-rev-list $count $rev_list_args" |
|
||||
eval "git-diff-tree --stdin --pretty -r $diff_tree_flags $diff_tree_args" |
|
||||
LESS="$LESS -S" ${PAGER:-less}
|
Загрузка…
Ссылка в новой задаче