
27 changed files with 20 additions and 8137 deletions
@ -1,3 +1,20 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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} |
Loading…
Reference in new issue