commit - 7ca04879e263ade3c559033b1188538df6e62478
commit + 1ebedb77e0d68bff22163a9aafc13db5f3f706df
blob - 07c1a76b07c5e373591b4238f041c215199048a2
blob + 703e42f8dad320122e89dcebabfda4f26a60a497
--- got/got.1
+++ got/got.1
.It \(a~ Ta versioned file is obstructed by a non-regular file
.It ? Ta unversioned item not tracked by
.Nm
+.It m Ta modified file modes (executable bit only)
.It N Ta non-existent
.Ar path
specified on the command line
.It M Ta modified file
.It D Ta file was deleted
.It A Ta new file was added
+.It m Ta modified file modes (executable bit only)
.El
.Pp
Files which are not part of the new commit will retain their previously
blob - add6c8aa88f249f82d6451270fc7c7c6952abc9b
blob + 336553780903efcf81551b9db8c4a76a0ab2f148
--- include/got_worktree.h
+++ include/got_worktree.h
#define GOT_STATUS_UPDATE 'U'
#define GOT_STATUS_DELETE 'D'
#define GOT_STATUS_MODIFY 'M'
+#define GOT_STATUS_MODE_CHANGE 'm'
#define GOT_STATUS_CONFLICT 'C'
#define GOT_STATUS_MERGE 'G'
#define GOT_STATUS_MISSING '!'
blob - 35d6705944c9ba45ca47f53a0109f5cc466e5957
blob + 62fe20f7905b2378140690fcaec185c0abae22d3
--- lib/worktree.c
+++ lib/worktree.c
err = got_fileindex_entry_add(fileindex, new_ie);
}
return err;
+}
+
+static mode_t
+get_ondisk_perms(int executable, mode_t st_mode)
+{
+ mode_t xbits = S_IXUSR;
+
+ if (executable) {
+ /* Map read bits to execute bits. */
+ if (st_mode & S_IRGRP)
+ xbits |= S_IXGRP;
+ if (st_mode & S_IROTH)
+ xbits |= S_IXOTH;
+ return st_mode | xbits;
+ }
+
+ return (st_mode & ~(S_IXUSR | S_IXGRP | S_IXOTH));
}
static const struct got_error *
}
}
- if (te_mode & S_IXUSR) {
- if (chmod(ondisk_path, st_mode | S_IXUSR) == -1) {
- err = got_error_from_errno2("chmod", ondisk_path);
- goto done;
- }
- } else {
- if (chmod(ondisk_path, st_mode & ~S_IXUSR) == -1) {
- err = got_error_from_errno2("chmod", ondisk_path);
- goto done;
- }
+ if (chmod(ondisk_path,
+ get_ondisk_perms(te_mode & S_IXUSR, st_mode)) == -1) {
+ err = got_error_from_errno2("chmod", ondisk_path);
+ goto done;
}
done:
}
static int
+xbit_differs(struct got_fileindex_entry *ie, uint16_t st_mode)
+{
+ mode_t ie_mode = got_fileindex_perms_to_st(ie);
+ return ((ie_mode & S_IXUSR) != (st_mode & S_IXUSR));
+}
+
+static int
stat_info_differs(struct got_fileindex_entry *ie, struct stat *sb)
{
return !(ie->ctime_sec == sb->st_ctime &&
ie->ctime_nsec == sb->st_ctimensec &&
ie->mtime_sec == sb->st_mtime &&
ie->mtime_nsec == sb->st_mtimensec &&
- ie->size == (sb->st_size & 0xffffffff));
+ ie->size == (sb->st_size & 0xffffffff) &&
+ !xbit_differs(ie, sb->st_mode));
}
static unsigned char
if (*status == GOT_STATUS_MODIFY) {
rewind(f);
err = get_modified_file_content_status(status, f);
- }
+ } else if (xbit_differs(ie, sb->st_mode))
+ *status = GOT_STATUS_MODE_CHANGE;
done:
if (blob)
got_object_blob_close(blob);
err = got_fileindex_entry_update(ie, ondisk_path,
blob->id.sha1, worktree->base_commit_id->sha1,
update_timestamps);
+ } else if (status == GOT_STATUS_MODE_CHANGE) {
+ err = got_fileindex_entry_update(ie, ondisk_path,
+ blob->id.sha1, worktree->base_commit_id->sha1, 0);
} else if (status == GOT_STATUS_DELETE) {
err = (*progress_cb)(progress_arg, GOT_STATUS_MERGE, path);
if (err)
const struct got_error *err;
struct got_blob_object *blob = NULL;
FILE *f1 = NULL, *f2 = NULL, *outfile = NULL;
+ int fd2 = -1;
char *path1 = NULL, *id_str = NULL;
struct stat sb1, sb2;
struct got_diff_changes *changes = NULL;
if (err)
return err;
- f2 = fopen(path2, "r");
+ fd2 = open(path2, O_RDONLY | O_NOFOLLOW);
+ if (fd2 == -1) {
+ err = got_error_from_errno2("open", path2);
+ goto done;
+ }
+ if (fstat(fd2, &sb2) == -1) {
+ err = got_error_from_errno2("fstat", path2);
+ goto done;
+ }
+
+ f2 = fdopen(fd2, "r");
if (f2 == NULL) {
err = got_error_from_errno2("fopen", path2);
goto done;
}
+ fd2 = -1;
err = got_object_open_as_blob(&blob, repo, blob_id, 8192);
if (err)
err = got_error_from_errno2("stat", path1);
goto done;
}
- if (stat(path2, &sb2) == -1) {
- err = got_error_from_errno2("stat", path2);
- goto done;
- }
err = got_diff_files(&changes, &ds, &args, &diff_flags,
f1, sb1.st_size, id_str, f2, sb2.st_size, path2, 3, NULL);
else if (choice == GOT_PATCH_CHOICE_QUIT)
break;
}
- if (have_content)
+ if (have_content) {
err = copy_remaining_content(f1, f2, &line_cur1, &line_cur2,
reverse_patch ? NULL : outfile,
reverse_patch ? outfile : NULL);
+ if (err)
+ goto done;
+
+ if (chmod(*path_outfile, sb2.st_mode) == -1) {
+ err = got_error_from_errno2("chmod", path2);
+ goto done;
+ }
+ }
done:
free(id_str);
if (blob)
err = got_error_from_errno2("fclose", path1);
if (f2 && fclose(f2) == EOF && err == NULL)
err = got_error_from_errno2("fclose", path2);
+ if (fd2 != -1 && close(fd2) == -1 && err == NULL)
+ err = got_error_from_errno2("close", path2);
if (outfile && fclose(outfile) == EOF && err == NULL)
err = got_error_from_errno2("fclose", *path_outfile);
if (path1 && unlink(path1) == -1 && err == NULL)
}
/* fall through */
case GOT_STATUS_MODIFY:
+ case GOT_STATUS_MODE_CHANGE:
case GOT_STATUS_CONFLICT:
case GOT_STATUS_MISSING: {
struct got_object_id id;
a->repo, a->progress_cb, a->progress_arg);
if (err)
goto done;
- if (status == GOT_STATUS_DELETE) {
+ if (status == GOT_STATUS_DELETE ||
+ status == GOT_STATUS_MODE_CHANGE) {
err = update_blob_fileindex_entry(a->worktree,
a->fileindex, ie, ondisk_path, ie->path,
blob, 1);
return got_error(GOT_ERR_COMMIT_CONFLICT);
if (status != GOT_STATUS_MODIFY &&
+ status != GOT_STATUS_MODE_CHANGE &&
status != GOT_STATUS_ADD &&
status != GOT_STATUS_DELETE)
return NULL;
if (ct->staged_status == GOT_STATUS_NO_CHANGE) {
if (ct->status != GOT_STATUS_MODIFY &&
+ ct->status != GOT_STATUS_MODE_CHANGE &&
ct->status != GOT_STATUS_DELETE)
continue;
} else {
if (ct) {
/* NB: Deleted entries get dropped here. */
if (ct->status == GOT_STATUS_MODIFY ||
+ ct->status == GOT_STATUS_MODE_CHANGE ||
ct->staged_status == GOT_STATUS_MODIFY) {
err = alloc_modified_blob_tree_entry(
&new_te, te, ct);
continue;
if (ct->status != GOT_STATUS_ADD &&
- ct->status != GOT_STATUS_MODIFY)
+ ct->status != GOT_STATUS_MODIFY &&
+ ct->status != GOT_STATUS_MODE_CHANGE)
continue;
if (asprintf(&ondisk_path, "%s/%s",
blob - 55340c74b234a5573505c25f1137cc947e5d43d5
blob + fbf48ee5570790ff88f8e64e0c4f1a1aab82289f
--- regress/cmdline/commit.sh
+++ regress/cmdline/commit.sh
fi
test_done "$testroot" "$ret"
}
+
+function test_commit_xbit_change {
+ local testroot=`test_init commit_xbit_change`
+
+ got checkout $testroot/repo $testroot/wt > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ chmod +x $testroot/wt/alpha
+
+ echo 'm alpha' > $testroot/stdout.expected
+ (cd $testroot/wt && got status > $testroot/stdout)
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ (cd $testroot/wt && got commit -mx > $testroot/stdout)
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ echo "got commit failed unexpectedly"
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ local commit_id=`git_show_head $testroot/repo`
+ echo 'm alpha' > $testroot/stdout.expected
+ echo "Created commit $commit_id" >> $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ (cd $testroot/wt && got status > $testroot/stdout)
+
+ echo -n > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ chmod -x $testroot/wt/alpha
+
+ echo 'm alpha' > $testroot/stdout.expected
+ (cd $testroot/wt && got status > $testroot/stdout)
+
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ (cd $testroot/wt && got commit -mx > $testroot/stdout)
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ echo "got commit failed unexpectedly"
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ local commit_id=`git_show_head $testroot/repo`
+ echo 'm alpha' > $testroot/stdout.expected
+ echo "Created commit $commit_id" >> $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ chmod +x $testroot/wt/alpha
+
+ echo 'm alpha' > $testroot/stdout.expected
+ (cd $testroot/wt && got status > $testroot/stdout)
+
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ fi
+ test_done "$testroot" "$ret"
+}
+
run_test test_commit_basic
run_test test_commit_new_subdir
run_test test_commit_subdir
run_test test_commit_no_email
run_test test_commit_tree_entry_sorting
run_test test_commit_gitconfig_author
+run_test test_commit_xbit_change