Merge branch 'jc/unresolve' into next
* jc/unresolve: Add git-unresolve <paths>... get_tree_entry(): make it available from tree-walk sha1_name.c: no need to include diff.h; tree-walk.h will do. sha1_name.c: prepare to make get_tree_entry() reusable from others. pre-commit hook: complain about conflict markers. git-merge: a bit more readable user guidance. diff: move diff.c to diff-lib.c to make room. git log: don't do merge diffs by default Allow "git repack" users to specify repacking window/depth
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -111,6 +111,7 @@ git-tag
|
|||||||
git-tar-tree
|
git-tar-tree
|
||||||
git-unpack-file
|
git-unpack-file
|
||||||
git-unpack-objects
|
git-unpack-objects
|
||||||
|
git-unresolve
|
||||||
git-update-index
|
git-update-index
|
||||||
git-update-ref
|
git-update-ref
|
||||||
git-update-server-info
|
git-update-server-info
|
||||||
|
5
Makefile
5
Makefile
@ -165,7 +165,8 @@ PROGRAMS = \
|
|||||||
git-upload-pack$X git-verify-pack$X git-write-tree$X \
|
git-upload-pack$X git-verify-pack$X git-write-tree$X \
|
||||||
git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \
|
git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \
|
||||||
git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
|
git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
|
||||||
git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
|
git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \
|
||||||
|
git-unresolve$X
|
||||||
|
|
||||||
BUILT_INS = git-log$X
|
BUILT_INS = git-log$X
|
||||||
|
|
||||||
@ -199,7 +200,7 @@ LIB_H = \
|
|||||||
tree-walk.h log-tree.h
|
tree-walk.h log-tree.h
|
||||||
|
|
||||||
DIFF_OBJS = \
|
DIFF_OBJS = \
|
||||||
diff.o diffcore-break.o diffcore-order.o \
|
diff-lib.o diffcore-break.o diffcore-order.o \
|
||||||
diffcore-pickaxe.o diffcore-rename.o tree-diff.o combine-diff.o \
|
diffcore-pickaxe.o diffcore-rename.o tree-diff.o combine-diff.o \
|
||||||
diffcore-delta.o log-tree.o
|
diffcore-delta.o log-tree.o
|
||||||
|
|
||||||
|
@ -335,5 +335,5 @@ Conflicts:
|
|||||||
then
|
then
|
||||||
git-rerere
|
git-rerere
|
||||||
fi
|
fi
|
||||||
die "Automatic merge failed; fix up by hand"
|
die "Automatic merge failed; fix conflicts and then commit the result."
|
||||||
fi
|
fi
|
||||||
|
@ -7,7 +7,7 @@ USAGE='[-a] [-d] [-f] [-l] [-n] [-q]'
|
|||||||
. git-sh-setup
|
. git-sh-setup
|
||||||
|
|
||||||
no_update_info= all_into_one= remove_redundant=
|
no_update_info= all_into_one= remove_redundant=
|
||||||
local= quiet= no_reuse_delta=
|
local= quiet= no_reuse_delta= extra=
|
||||||
while case "$#" in 0) break ;; esac
|
while case "$#" in 0) break ;; esac
|
||||||
do
|
do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@ -17,6 +17,8 @@ do
|
|||||||
-q) quiet=-q ;;
|
-q) quiet=-q ;;
|
||||||
-f) no_reuse_delta=--no-reuse-delta ;;
|
-f) no_reuse_delta=--no-reuse-delta ;;
|
||||||
-l) local=--local ;;
|
-l) local=--local ;;
|
||||||
|
--window=*) extra="$extra $1" ;;
|
||||||
|
--depth=*) extra="$extra $1" ;;
|
||||||
*) usage ;;
|
*) usage ;;
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
@ -40,7 +42,7 @@ case ",$all_into_one," in
|
|||||||
find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
|
find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
pack_objects="$pack_objects $local $quiet $no_reuse_delta"
|
pack_objects="$pack_objects $local $quiet $no_reuse_delta$extra"
|
||||||
name=$(git-rev-list --objects --all $rev_list 2>&1 |
|
name=$(git-rev-list --objects --all $rev_list 2>&1 |
|
||||||
git-pack-objects --non-empty $pack_objects .tmp-pack) ||
|
git-pack-objects --non-empty $pack_objects .tmp-pack) ||
|
||||||
exit 1
|
exit 1
|
||||||
|
2
git.c
2
git.c
@ -368,8 +368,6 @@ static int cmd_log(int argc, const char **argv, char **envp)
|
|||||||
init_revisions(&rev);
|
init_revisions(&rev);
|
||||||
rev.always_show_header = 1;
|
rev.always_show_header = 1;
|
||||||
rev.diffopt.recursive = 1;
|
rev.diffopt.recursive = 1;
|
||||||
rev.combine_merges = 1;
|
|
||||||
rev.ignore_merges = 0;
|
|
||||||
return cmd_log_wc(argc, argv, envp, &rev);
|
return cmd_log_wc(argc, argv, envp, &rev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
59
sha1_name.c
59
sha1_name.c
@ -3,7 +3,7 @@
|
|||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "diff.h"
|
#include "tree-walk.h"
|
||||||
|
|
||||||
static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
|
static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
|
||||||
{
|
{
|
||||||
@ -450,59 +450,6 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
|
|||||||
return get_short_sha1(name, len, sha1, 0);
|
return get_short_sha1(name, len, sha1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_tree_entry(const unsigned char *, const char *, unsigned char *);
|
|
||||||
|
|
||||||
static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result)
|
|
||||||
{
|
|
||||||
int namelen = strlen(name);
|
|
||||||
while (t->size) {
|
|
||||||
const char *entry;
|
|
||||||
const unsigned char *sha1;
|
|
||||||
int entrylen, cmp;
|
|
||||||
unsigned mode;
|
|
||||||
|
|
||||||
sha1 = tree_entry_extract(t, &entry, &mode);
|
|
||||||
update_tree_entry(t);
|
|
||||||
entrylen = strlen(entry);
|
|
||||||
if (entrylen > namelen)
|
|
||||||
continue;
|
|
||||||
cmp = memcmp(name, entry, entrylen);
|
|
||||||
if (cmp > 0)
|
|
||||||
continue;
|
|
||||||
if (cmp < 0)
|
|
||||||
break;
|
|
||||||
if (entrylen == namelen) {
|
|
||||||
memcpy(result, sha1, 20);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (name[entrylen] != '/')
|
|
||||||
continue;
|
|
||||||
if (!S_ISDIR(mode))
|
|
||||||
break;
|
|
||||||
if (++entrylen == namelen) {
|
|
||||||
memcpy(result, sha1, 20);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return get_tree_entry(sha1, name + entrylen, result);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned char *sha1)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
void *tree;
|
|
||||||
struct tree_desc t;
|
|
||||||
|
|
||||||
tree = read_object_with_reference(tree_sha1, tree_type, &t.size, NULL);
|
|
||||||
if (!tree)
|
|
||||||
return -1;
|
|
||||||
t.buf = tree;
|
|
||||||
retval = find_tree_entry(&t, name, sha1);
|
|
||||||
free(tree);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is like "get_sha1_basic()", except it allows "sha1 expressions",
|
* This is like "get_sha1_basic()", except it allows "sha1 expressions",
|
||||||
* notably "xyz^" for "parent of xyz"
|
* notably "xyz^" for "parent of xyz"
|
||||||
@ -510,6 +457,7 @@ static int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsi
|
|||||||
int get_sha1(const char *name, unsigned char *sha1)
|
int get_sha1(const char *name, unsigned char *sha1)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
unsigned unused;
|
||||||
|
|
||||||
prepare_alt_odb();
|
prepare_alt_odb();
|
||||||
ret = get_sha1_1(name, strlen(name), sha1);
|
ret = get_sha1_1(name, strlen(name), sha1);
|
||||||
@ -518,7 +466,8 @@ int get_sha1(const char *name, unsigned char *sha1)
|
|||||||
if (cp) {
|
if (cp) {
|
||||||
unsigned char tree_sha1[20];
|
unsigned char tree_sha1[20];
|
||||||
if (!get_sha1_1(name, cp-name, tree_sha1))
|
if (!get_sha1_1(name, cp-name, tree_sha1))
|
||||||
return get_tree_entry(tree_sha1, cp+1, sha1);
|
return get_tree_entry(tree_sha1, cp+1, sha1,
|
||||||
|
&unused);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -61,6 +61,9 @@ perl -e '
|
|||||||
if (/^\s* /) {
|
if (/^\s* /) {
|
||||||
bad_line("indent SP followed by a TAB", $_);
|
bad_line("indent SP followed by a TAB", $_);
|
||||||
}
|
}
|
||||||
|
if (/^(?:[<>=]){7}/) {
|
||||||
|
bad_line("unresolved merge conflict", $_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exit($found_bad);
|
exit($found_bad);
|
||||||
|
50
tree-walk.c
50
tree-walk.c
@ -115,3 +115,53 @@ void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callb
|
|||||||
free(entry);
|
free(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
|
||||||
|
{
|
||||||
|
int namelen = strlen(name);
|
||||||
|
while (t->size) {
|
||||||
|
const char *entry;
|
||||||
|
const unsigned char *sha1;
|
||||||
|
int entrylen, cmp;
|
||||||
|
|
||||||
|
sha1 = tree_entry_extract(t, &entry, mode);
|
||||||
|
update_tree_entry(t);
|
||||||
|
entrylen = strlen(entry);
|
||||||
|
if (entrylen > namelen)
|
||||||
|
continue;
|
||||||
|
cmp = memcmp(name, entry, entrylen);
|
||||||
|
if (cmp > 0)
|
||||||
|
continue;
|
||||||
|
if (cmp < 0)
|
||||||
|
break;
|
||||||
|
if (entrylen == namelen) {
|
||||||
|
memcpy(result, sha1, 20);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (name[entrylen] != '/')
|
||||||
|
continue;
|
||||||
|
if (!S_ISDIR(*mode))
|
||||||
|
break;
|
||||||
|
if (++entrylen == namelen) {
|
||||||
|
memcpy(result, sha1, 20);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return get_tree_entry(sha1, name + entrylen, result, mode);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned char *sha1, unsigned *mode)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
void *tree;
|
||||||
|
struct tree_desc t;
|
||||||
|
|
||||||
|
tree = read_object_with_reference(tree_sha1, tree_type, &t.size, NULL);
|
||||||
|
if (!tree)
|
||||||
|
return -1;
|
||||||
|
t.buf = tree;
|
||||||
|
retval = find_tree_entry(&t, name, sha1, mode);
|
||||||
|
free(tree);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -22,4 +22,6 @@ typedef void (*traverse_callback_t)(int n, unsigned long mask, struct name_entry
|
|||||||
|
|
||||||
void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback);
|
void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback);
|
||||||
|
|
||||||
|
int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
146
unresolve.c
Normal file
146
unresolve.c
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
#include "cache.h"
|
||||||
|
#include "tree-walk.h"
|
||||||
|
|
||||||
|
static const char unresolve_usage[] =
|
||||||
|
"git-unresolve <paths>...";
|
||||||
|
|
||||||
|
static struct cache_file cache_file;
|
||||||
|
static unsigned char head_sha1[20];
|
||||||
|
static unsigned char merge_head_sha1[20];
|
||||||
|
|
||||||
|
static struct cache_entry *read_one_ent(const char *which,
|
||||||
|
unsigned char *ent, const char *path,
|
||||||
|
int namelen, int stage)
|
||||||
|
{
|
||||||
|
unsigned mode;
|
||||||
|
unsigned char sha1[20];
|
||||||
|
int size;
|
||||||
|
struct cache_entry *ce;
|
||||||
|
|
||||||
|
if (get_tree_entry(ent, path, sha1, &mode)) {
|
||||||
|
error("%s: not in %s branch.", path, which);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (mode == S_IFDIR) {
|
||||||
|
error("%s: not a blob in %s branch.", path, which);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
size = cache_entry_size(namelen);
|
||||||
|
ce = xcalloc(1, size);
|
||||||
|
|
||||||
|
memcpy(ce->sha1, sha1, 20);
|
||||||
|
memcpy(ce->name, path, namelen);
|
||||||
|
ce->ce_flags = create_ce_flags(namelen, stage);
|
||||||
|
ce->ce_mode = create_ce_mode(mode);
|
||||||
|
return ce;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unresolve_one(const char *path)
|
||||||
|
{
|
||||||
|
int namelen = strlen(path);
|
||||||
|
int pos;
|
||||||
|
int ret = 0;
|
||||||
|
struct cache_entry *ce_2 = NULL, *ce_3 = NULL;
|
||||||
|
|
||||||
|
/* See if there is such entry in the index. */
|
||||||
|
pos = cache_name_pos(path, namelen);
|
||||||
|
if (pos < 0) {
|
||||||
|
/* If there isn't, either it is unmerged, or
|
||||||
|
* resolved as "removed" by mistake. We do not
|
||||||
|
* want to do anything in the former case.
|
||||||
|
*/
|
||||||
|
pos = -pos-1;
|
||||||
|
if (pos < active_nr) {
|
||||||
|
struct cache_entry *ce = active_cache[pos];
|
||||||
|
if (ce_namelen(ce) == namelen &&
|
||||||
|
!memcmp(ce->name, path, namelen)) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s: skipping still unmerged path.\n",
|
||||||
|
path);
|
||||||
|
goto free_return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grab blobs from given path from HEAD and MERGE_HEAD,
|
||||||
|
* stuff HEAD version in stage #2,
|
||||||
|
* stuff MERGE_HEAD version in stage #3.
|
||||||
|
*/
|
||||||
|
ce_2 = read_one_ent("our", head_sha1, path, namelen, 2);
|
||||||
|
ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3);
|
||||||
|
|
||||||
|
if (!ce_2 || !ce_3) {
|
||||||
|
ret = -1;
|
||||||
|
goto free_return;
|
||||||
|
}
|
||||||
|
if (!memcmp(ce_2->sha1, ce_3->sha1, 20) &&
|
||||||
|
ce_2->ce_mode == ce_3->ce_mode) {
|
||||||
|
fprintf(stderr, "%s: identical in both, skipping.\n",
|
||||||
|
path);
|
||||||
|
goto free_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_file_from_cache(path);
|
||||||
|
if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
|
||||||
|
error("%s: cannot add our version to the index.", path);
|
||||||
|
ret = -1;
|
||||||
|
goto free_return;
|
||||||
|
}
|
||||||
|
if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD))
|
||||||
|
return 0;
|
||||||
|
error("%s: cannot add their version to the index.", path);
|
||||||
|
ret = -1;
|
||||||
|
free_return:
|
||||||
|
free(ce_2);
|
||||||
|
free(ce_3);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void read_head_pointers(void)
|
||||||
|
{
|
||||||
|
if (read_ref(git_path("HEAD"), head_sha1))
|
||||||
|
die("Cannot read HEAD -- no initial commit yet?");
|
||||||
|
if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) {
|
||||||
|
fprintf(stderr, "Not in the middle of a merge.\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int ac, char **av)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int err = 0;
|
||||||
|
int newfd;
|
||||||
|
|
||||||
|
if (ac < 2)
|
||||||
|
usage(unresolve_usage);
|
||||||
|
|
||||||
|
git_config(git_default_config);
|
||||||
|
|
||||||
|
/* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we
|
||||||
|
* are not doing a merge, so exit with success status.
|
||||||
|
*/
|
||||||
|
read_head_pointers();
|
||||||
|
|
||||||
|
/* Otherwise we would need to update the cache. */
|
||||||
|
newfd= hold_index_file_for_update(&cache_file, get_index_file());
|
||||||
|
if (newfd < 0)
|
||||||
|
die("unable to create new cachefile");
|
||||||
|
|
||||||
|
if (read_cache() < 0)
|
||||||
|
die("cache corrupted");
|
||||||
|
|
||||||
|
for (i = 1; i < ac; i++) {
|
||||||
|
char *arg = av[i];
|
||||||
|
err |= unresolve_one(arg);
|
||||||
|
}
|
||||||
|
if (err)
|
||||||
|
die("Error encountered; index not updated.");
|
||||||
|
|
||||||
|
if (active_cache_changed) {
|
||||||
|
if (write_cache(newfd, active_cache, active_nr) ||
|
||||||
|
commit_index_file(&cache_file))
|
||||||
|
die("Unable to write new cachefile");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
Reference in New Issue
Block a user