aboutsummaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/buildsystems/CMakeLists.txt3
-rw-r--r--contrib/coccinelle/xstrncmpz.cocci28
-rw-r--r--contrib/completion/git-completion.bash119
-rw-r--r--contrib/completion/git-completion.zsh1
-rw-r--r--contrib/completion/git-prompt.sh4
-rwxr-xr-xcontrib/coverage-diff.sh9
-rw-r--r--contrib/credential/libsecret/git-credential-libsecret.c3
-rw-r--r--contrib/credential/osxkeychain/Makefile3
-rw-r--r--contrib/credential/osxkeychain/git-credential-osxkeychain.c376
-rwxr-xr-xcontrib/hg-to-git/hg-to-git.py254
-rw-r--r--contrib/hg-to-git/hg-to-git.txt21
-rwxr-xr-xcontrib/subtree/t/t7900-subtree.sh2
-rwxr-xr-xcontrib/vscode/init.sh1
-rwxr-xr-xcontrib/workdir/git-new-workdir2
14 files changed, 451 insertions, 375 deletions
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 804629c525..2f9c33585c 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -1005,10 +1005,11 @@ endforeach()
#test-tool
parse_makefile_for_sources(test-tool_SOURCES "TEST_BUILTINS_OBJS")
+add_library(test-lib OBJECT ${CMAKE_SOURCE_DIR}/t/unit-tests/test-lib.c)
list(TRANSFORM test-tool_SOURCES PREPEND "${CMAKE_SOURCE_DIR}/t/helper/")
add_executable(test-tool ${CMAKE_SOURCE_DIR}/t/helper/test-tool.c ${test-tool_SOURCES} ${test-reftable_SOURCES})
-target_link_libraries(test-tool common-main)
+target_link_libraries(test-tool test-lib common-main)
set_target_properties(test-fake-ssh test-tool
PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/t/helper)
diff --git a/contrib/coccinelle/xstrncmpz.cocci b/contrib/coccinelle/xstrncmpz.cocci
new file mode 100644
index 0000000000..ccb39e2bc0
--- /dev/null
+++ b/contrib/coccinelle/xstrncmpz.cocci
@@ -0,0 +1,28 @@
+@@
+expression S, T, L;
+@@
+(
+- strncmp(S, T, L) || S[L]
++ !!xstrncmpz(S, T, L)
+|
+- strncmp(S, T, L) || S[L] != '\0'
++ !!xstrncmpz(S, T, L)
+|
+- strncmp(S, T, L) || T[L]
++ !!xstrncmpz(T, S, L)
+|
+- strncmp(S, T, L) || T[L] != '\0'
++ !!xstrncmpz(T, S, L)
+|
+- !strncmp(S, T, L) && !S[L]
++ !xstrncmpz(S, T, L)
+|
+- !strncmp(S, T, L) && S[L] == '\0'
++ !xstrncmpz(S, T, L)
+|
+- !strncmp(S, T, L) && !T[L]
++ !xstrncmpz(T, S, L)
+|
+- !strncmp(S, T, L) && T[L] == '\0'
++ !xstrncmpz(T, S, L)
+)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 444b3efa63..5c0ddeb3d4 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -31,15 +31,29 @@
# Note that "git" is optional --- '!f() { : commit; ...}; f' would complete
# just like the 'git commit' command.
#
-# If you have a command that is not part of git, but you would still
-# like completion, you can use __git_complete:
+# To add completion for git subcommands that are implemented in external
+# scripts, define a function of the form '_git_${subcommand}' while replacing
+# all dashes with underscores, and the main git completion will make use of it.
+# For example, to add completion for 'git do-stuff' (which could e.g. live
+# in /usr/bin/git-do-stuff), name the completion function '_git_do_stuff'.
+# See _git_show, _git_bisect etc. below for more examples.
+#
+# If you have a shell command that is not part of git (and is not called as a
+# git subcommand), but you would still like git-style completion for it, use
+# __git_complete. For example, to use the same completion as for 'git log' also
+# for the 'gl' command:
#
# __git_complete gl git_log
#
-# Or if it's a main command (i.e. git or gitk):
+# Or if the 'gk' command should be completed the same as 'gitk':
#
# __git_complete gk gitk
#
+# The second parameter of __git_complete gives the completion function; it is
+# resolved as a function named "$2", or "__$2_main", or "_$2" in that order.
+# In the examples above, the actual functions used for completion will be
+# _git_log and __gitk_main.
+#
# Compatible with bash 3.2.57.
#
# You can set the following environment variables to influence the behavior of
@@ -454,16 +468,18 @@ fi
# This function is equivalent to
#
-# __gitcomp "$(git xxx --git-completion-helper) ..."
+# ___git_resolved_builtins=$(git xxx --git-completion-helper)
#
-# except that the output is cached. Accept 1-3 arguments:
+# except that the result of the execution is cached.
+#
+# Accept 1-3 arguments:
# 1: the git command to execute, this is also the cache key
+# (use "_" when the command contains spaces, e.g. "remote add"
+# becomes "remote_add")
# 2: extra options to be added on top (e.g. negative forms)
# 3: options to be excluded
-__gitcomp_builtin ()
+__git_resolve_builtins ()
{
- # spaces must be replaced with underscore for multi-word
- # commands, e.g. "git remote add" becomes remote_add.
local cmd="$1"
local incl="${2-}"
local excl="${3-}"
@@ -489,7 +505,24 @@ __gitcomp_builtin ()
eval "$var=\"$options\""
fi
- __gitcomp "$options"
+ ___git_resolved_builtins="$options"
+}
+
+# This function is equivalent to
+#
+# __gitcomp "$(git xxx --git-completion-helper) ..."
+#
+# except that the output is cached. Accept 1-3 arguments:
+# 1: the git command to execute, this is also the cache key
+# (use "_" when the command contains spaces, e.g. "remote add"
+# becomes "remote_add")
+# 2: extra options to be added on top (e.g. negative forms)
+# 3: options to be excluded
+__gitcomp_builtin ()
+{
+ __git_resolve_builtins "$1" "$2" "$3"
+
+ __gitcomp "$___git_resolved_builtins"
}
# Variation of __gitcomp_nl () that appends to the existing list of
@@ -556,6 +589,26 @@ __gitcomp_file ()
true
}
+# Find the current subcommand for commands that follow the syntax:
+#
+# git <command> <subcommand>
+#
+# 1: List of possible subcommands.
+# 2: Optional subcommand to return when none is found.
+__git_find_subcommand ()
+{
+ local subcommand subcommands="$1" default_subcommand="$2"
+
+ for subcommand in $subcommands; do
+ if [ "$subcommand" = "${words[__git_cmd_idx+1]}" ]; then
+ echo $subcommand
+ return
+ fi
+ done
+
+ echo $default_subcommand
+}
+
# Execute 'git ls-files', unless the --committable option is specified, in
# which case it runs 'git diff-index' to find out the files that can be
# committed. It return paths relative to the directory specified in the first
@@ -2471,13 +2524,30 @@ _git_rebase ()
_git_reflog ()
{
- local subcommands="show delete expire"
- local subcommand="$(__git_find_on_cmdline "$subcommands")"
+ local subcommands subcommand
- if [ -z "$subcommand" ]; then
- __gitcomp "$subcommands"
- else
- __git_complete_refs
+ __git_resolve_builtins "reflog"
+
+ subcommands="$___git_resolved_builtins"
+ subcommand="$(__git_find_subcommand "$subcommands" "show")"
+
+ case "$subcommand,$cur" in
+ show,--*)
+ __gitcomp "
+ $__git_log_common_options
+ "
+ return
+ ;;
+ $subcommand,--*)
+ __gitcomp_builtin "reflog_$subcommand"
+ return
+ ;;
+ esac
+
+ __git_complete_refs
+
+ if [ $((cword - __git_cmd_idx)) -eq 1 ]; then
+ __gitcompappend "$subcommands" "" "$cur" " "
fi
}
@@ -2673,7 +2743,8 @@ __git_compute_first_level_config_vars_for_section ()
__git_compute_config_vars
local this_section="__git_first_level_config_vars_for_section_${section}"
test -n "${!this_section}" ||
- printf -v "__git_first_level_config_vars_for_section_${section}" %s "$(echo "$__git_config_vars" | grep -E "^${section}\.[a-z]" | awk -F. '{print $2}')"
+ printf -v "__git_first_level_config_vars_for_section_${section}" %s \
+ "$(echo "$__git_config_vars" | awk -F. "/^${section}\.[a-z]/ { print \$2 }")"
}
__git_compute_second_level_config_vars_for_section ()
@@ -2682,7 +2753,8 @@ __git_compute_second_level_config_vars_for_section ()
__git_compute_config_vars_all
local this_section="__git_second_level_config_vars_for_section_${section}"
test -n "${!this_section}" ||
- printf -v "__git_second_level_config_vars_for_section_${section}" %s "$(echo "$__git_config_vars_all" | grep -E "^${section}\.<" | awk -F. '{print $3}')"
+ printf -v "__git_second_level_config_vars_for_section_${section}" %s \
+ "$(echo "$__git_config_vars_all" | awk -F. "/^${section}\.</ { print \$3 }")"
}
__git_config_sections=
@@ -3523,6 +3595,17 @@ _git_svn ()
fi
}
+_git_symbolic_ref () {
+ case "$cur" in
+ --*)
+ __gitcomp_builtin symbolic-ref
+ return
+ ;;
+ esac
+
+ __git_complete_refs
+}
+
_git_tag ()
{
local i c="$__git_cmd_idx" f=0
@@ -3571,7 +3654,7 @@ __git_complete_worktree_paths ()
# Generate completion reply from worktree list skipping the first
# entry: it's the path of the main worktree, which can't be moved,
# removed, locked, etc.
- __gitcomp_nl "$(git worktree list --porcelain |
+ __gitcomp_nl "$(__git worktree list --porcelain |
sed -n -e '2,$ s/^worktree //p')"
}
diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
index cac6f61881..f5877bd7a1 100644
--- a/contrib/completion/git-completion.zsh
+++ b/contrib/completion/git-completion.zsh
@@ -272,6 +272,7 @@ _git ()
{
local _ret=1
local cur cword prev
+ local __git_repo_path
cur=${words[CURRENT]}
prev=${words[CURRENT-1]}
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 71f179cba3..5330e769a7 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -141,7 +141,7 @@ __git_ps1_show_upstream ()
# parse configuration values
local option
- for option in ${GIT_PS1_SHOWUPSTREAM}; do
+ for option in ${GIT_PS1_SHOWUPSTREAM-}; do
case "$option" in
git|svn) upstream_type="$option" ;;
verbose) verbose=1 ;;
@@ -528,7 +528,7 @@ __git_ps1 ()
fi
local conflict="" # state indicator for unresolved conflicts
- if [[ "${GIT_PS1_SHOWCONFLICTSTATE}" == "yes" ]] &&
+ if [[ "${GIT_PS1_SHOWCONFLICTSTATE-}" == "yes" ]] &&
[[ $(git ls-files --unmerged 2>/dev/null) ]]; then
conflict="|CONFLICT"
fi
diff --git a/contrib/coverage-diff.sh b/contrib/coverage-diff.sh
index 4ec419f900..6ce9603568 100755
--- a/contrib/coverage-diff.sh
+++ b/contrib/coverage-diff.sh
@@ -74,8 +74,7 @@ do
sort >uncovered_lines.txt
comm -12 uncovered_lines.txt new_lines.txt |
- sed -e 's/$/\)/' |
- sed -e 's/^/ /' >uncovered_new_lines.txt
+ sed -e 's/$/\)/' -e 's/^/ /' >uncovered_new_lines.txt
grep -q '[^[:space:]]' <uncovered_new_lines.txt &&
echo $file >>coverage-data.txt &&
@@ -91,11 +90,7 @@ cat coverage-data.txt
echo "Commits introducing uncovered code:"
-commit_list=$(cat coverage-data.txt |
- grep -E '^[0-9a-f]{7,} ' |
- awk '{print $1;}' |
- sort |
- uniq)
+commit_list=$(awk '/^[0-9a-f]{7,}/ { print $1 }' coverage-data.txt | sort -u)
(
for commit in $commit_list
diff --git a/contrib/credential/libsecret/git-credential-libsecret.c b/contrib/credential/libsecret/git-credential-libsecret.c
index 215a81d8ba..90034d0cf1 100644
--- a/contrib/credential/libsecret/git-credential-libsecret.c
+++ b/contrib/credential/libsecret/git-credential-libsecret.c
@@ -164,6 +164,9 @@ static int keyring_get(struct credential *c)
if (g_strv_length(parts) >= 1) {
g_free(c->password);
c->password = g_strdup(parts[0]);
+ } else {
+ g_free(c->password);
+ c->password = g_strdup("");
}
for (int i = 1; i < g_strv_length(parts); i++) {
if (g_str_has_prefix(parts[i], "password_expiry_utc=")) {
diff --git a/contrib/credential/osxkeychain/Makefile b/contrib/credential/osxkeychain/Makefile
index 4b3a08a2ba..238f5f8c36 100644
--- a/contrib/credential/osxkeychain/Makefile
+++ b/contrib/credential/osxkeychain/Makefile
@@ -8,7 +8,8 @@ CFLAGS = -g -O2 -Wall
-include ../../../config.mak
git-credential-osxkeychain: git-credential-osxkeychain.o
- $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -Wl,-framework -Wl,Security
+ $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) \
+ -framework Security -framework CoreFoundation
git-credential-osxkeychain.o: git-credential-osxkeychain.c
$(CC) -c $(CFLAGS) $<
diff --git a/contrib/credential/osxkeychain/git-credential-osxkeychain.c b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
index 5f2e5f16c8..6a40917b1e 100644
--- a/contrib/credential/osxkeychain/git-credential-osxkeychain.c
+++ b/contrib/credential/osxkeychain/git-credential-osxkeychain.c
@@ -3,14 +3,51 @@
#include <stdlib.h>
#include <Security/Security.h>
-static SecProtocolType protocol;
-static char *host;
-static char *path;
-static char *username;
-static char *password;
-static UInt16 port;
-
-__attribute__((format (printf, 1, 2)))
+#define ENCODING kCFStringEncodingUTF8
+static CFStringRef protocol; /* Stores constant strings - not memory managed */
+static CFStringRef host;
+static CFNumberRef port;
+static CFStringRef path;
+static CFStringRef username;
+static CFDataRef password;
+static CFDataRef password_expiry_utc;
+static CFDataRef oauth_refresh_token;
+
+static void clear_credential(void)
+{
+ if (host) {
+ CFRelease(host);
+ host = NULL;
+ }
+ if (port) {
+ CFRelease(port);
+ port = NULL;
+ }
+ if (path) {
+ CFRelease(path);
+ path = NULL;
+ }
+ if (username) {
+ CFRelease(username);
+ username = NULL;
+ }
+ if (password) {
+ CFRelease(password);
+ password = NULL;
+ }
+ if (password_expiry_utc) {
+ CFRelease(password_expiry_utc);
+ password_expiry_utc = NULL;
+ }
+ if (oauth_refresh_token) {
+ CFRelease(oauth_refresh_token);
+ oauth_refresh_token = NULL;
+ }
+}
+
+#define STRING_WITH_LENGTH(s) s, sizeof(s) - 1
+
+__attribute__((format (printf, 1, 2), __noreturn__))
static void die(const char *err, ...)
{
char msg[4096];
@@ -19,70 +56,199 @@ static void die(const char *err, ...)
vsnprintf(msg, sizeof(msg), err, params);
fprintf(stderr, "%s\n", msg);
va_end(params);
+ clear_credential();
exit(1);
}
-static void *xstrdup(const char *s1)
+static void *xmalloc(size_t len)
{
- void *ret = strdup(s1);
+ void *ret = malloc(len);
if (!ret)
die("Out of memory");
return ret;
}
-#define KEYCHAIN_ITEM(x) (x ? strlen(x) : 0), x
-#define KEYCHAIN_ARGS \
- NULL, /* default keychain */ \
- KEYCHAIN_ITEM(host), \
- 0, NULL, /* account domain */ \
- KEYCHAIN_ITEM(username), \
- KEYCHAIN_ITEM(path), \
- port, \
- protocol, \
- kSecAuthenticationTypeDefault
-
-static void write_item(const char *what, const char *buf, int len)
+static CFDictionaryRef create_dictionary(CFAllocatorRef allocator, ...)
+{
+ va_list args;
+ const void *key;
+ CFMutableDictionaryRef result;
+
+ result = CFDictionaryCreateMutable(allocator,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+
+
+ va_start(args, allocator);
+ while ((key = va_arg(args, const void *)) != NULL) {
+ const void *value;
+ value = va_arg(args, const void *);
+ if (value)
+ CFDictionarySetValue(result, key, value);
+ }
+ va_end(args);
+
+ return result;
+}
+
+#define CREATE_SEC_ATTRIBUTES(...) \
+ create_dictionary(kCFAllocatorDefault, \
+ kSecClass, kSecClassInternetPassword, \
+ kSecAttrServer, host, \
+ kSecAttrAccount, username, \
+ kSecAttrPath, path, \
+ kSecAttrPort, port, \
+ kSecAttrProtocol, protocol, \
+ kSecAttrAuthenticationType, \
+ kSecAttrAuthenticationTypeDefault, \
+ __VA_ARGS__);
+
+static void write_item(const char *what, const char *buf, size_t len)
{
printf("%s=", what);
fwrite(buf, 1, len, stdout);
putchar('\n');
}
-static void find_username_in_item(SecKeychainItemRef item)
+static void find_username_in_item(CFDictionaryRef item)
{
- SecKeychainAttributeList list;
- SecKeychainAttribute attr;
+ CFStringRef account_ref;
+ char *username_buf;
+ CFIndex buffer_len;
- list.count = 1;
- list.attr = &attr;
- attr.tag = kSecAccountItemAttr;
+ account_ref = CFDictionaryGetValue(item, kSecAttrAccount);
+ if (!account_ref)
+ {
+ write_item("username", "", 0);
+ return;
+ }
- if (SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL))
+ username_buf = (char *)CFStringGetCStringPtr(account_ref, ENCODING);
+ if (username_buf)
+ {
+ write_item("username", username_buf, strlen(username_buf));
return;
+ }
- write_item("username", attr.data, attr.length);
- SecKeychainItemFreeContent(&list, NULL);
+ /* If we can't get a CString pointer then
+ * we need to allocate our own buffer */
+ buffer_len = CFStringGetMaximumSizeForEncoding(
+ CFStringGetLength(account_ref), ENCODING) + 1;
+ username_buf = xmalloc(buffer_len);
+ if (CFStringGetCString(account_ref,
+ username_buf,
+ buffer_len,
+ ENCODING)) {
+ write_item("username", username_buf, buffer_len - 1);
+ }
+ free(username_buf);
}
-static void find_internet_password(void)
+static OSStatus find_internet_password(void)
{
- void *buf;
- UInt32 len;
- SecKeychainItemRef item;
+ CFDictionaryRef attrs;
+ CFDictionaryRef item;
+ CFDataRef data;
+ OSStatus result;
- if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, &len, &buf, &item))
- return;
+ attrs = CREATE_SEC_ATTRIBUTES(kSecMatchLimit, kSecMatchLimitOne,
+ kSecReturnAttributes, kCFBooleanTrue,
+ kSecReturnData, kCFBooleanTrue,
+ NULL);
+ result = SecItemCopyMatching(attrs, (CFTypeRef *)&item);
+ if (result) {
+ goto out;
+ }
+
+ data = CFDictionaryGetValue(item, kSecValueData);
- write_item("password", buf, len);
+ write_item("password",
+ (const char *)CFDataGetBytePtr(data),
+ CFDataGetLength(data));
if (!username)
find_username_in_item(item);
- SecKeychainItemFreeContent(NULL, buf);
+ CFRelease(item);
+
+out:
+ CFRelease(attrs);
+
+ /* We consider not found to not be an error */
+ if (result == errSecItemNotFound)
+ result = errSecSuccess;
+
+ return result;
+}
+
+static OSStatus delete_ref(const void *itemRef)
+{
+ CFArrayRef item_ref_list;
+ CFDictionaryRef delete_query;
+ OSStatus result;
+
+ item_ref_list = CFArrayCreate(kCFAllocatorDefault,
+ &itemRef,
+ 1,
+ &kCFTypeArrayCallBacks);
+ delete_query = create_dictionary(kCFAllocatorDefault,
+ kSecClass, kSecClassInternetPassword,
+ kSecMatchItemList, item_ref_list,
+ NULL);
+
+ if (password) {
+ /* We only want to delete items with a matching password */
+ CFIndex capacity;
+ CFMutableDictionaryRef query;
+ CFDataRef data;
+
+ capacity = CFDictionaryGetCount(delete_query) + 1;
+ query = CFDictionaryCreateMutableCopy(kCFAllocatorDefault,
+ capacity,
+ delete_query);
+ CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
+ result = SecItemCopyMatching(query, (CFTypeRef *)&data);
+ if (!result) {
+ CFDataRef kc_password;
+ const UInt8 *raw_data;
+ const UInt8 *line;
+
+ /* Don't match appended metadata */
+ raw_data = CFDataGetBytePtr(data);
+ line = memchr(raw_data, '\n', CFDataGetLength(data));
+ if (line)
+ kc_password = CFDataCreateWithBytesNoCopy(
+ kCFAllocatorDefault,
+ raw_data,
+ line - raw_data,
+ kCFAllocatorNull);
+ else
+ kc_password = data;
+
+ if (CFEqual(kc_password, password))
+ result = SecItemDelete(delete_query);
+
+ if (line)
+ CFRelease(kc_password);
+ CFRelease(data);
+ }
+
+ CFRelease(query);
+ } else {
+ result = SecItemDelete(delete_query);
+ }
+
+ CFRelease(delete_query);
+ CFRelease(item_ref_list);
+
+ return result;
}
-static void delete_internet_password(void)
+static OSStatus delete_internet_password(void)
{
- SecKeychainItemRef item;
+ CFDictionaryRef attrs;
+ CFArrayRef refs;
+ OSStatus result;
/*
* Require at least a protocol and host for removal, which is what git
@@ -90,25 +256,69 @@ static void delete_internet_password(void)
* Keychain manager.
*/
if (!protocol || !host)
- return;
+ return -1;
- if (SecKeychainFindInternetPassword(KEYCHAIN_ARGS, 0, NULL, &item))
- return;
+ attrs = CREATE_SEC_ATTRIBUTES(kSecMatchLimit, kSecMatchLimitAll,
+ kSecReturnRef, kCFBooleanTrue,
+ NULL);
+ result = SecItemCopyMatching(attrs, (CFTypeRef *)&refs);
+ CFRelease(attrs);
+
+ if (!result) {
+ for (CFIndex i = 0; !result && i < CFArrayGetCount(refs); i++)
+ result = delete_ref(CFArrayGetValueAtIndex(refs, i));
- SecKeychainItemDelete(item);
+ CFRelease(refs);
+ }
+
+ /* We consider not found to not be an error */
+ if (result == errSecItemNotFound)
+ result = errSecSuccess;
+
+ return result;
}
-static void add_internet_password(void)
+static OSStatus add_internet_password(void)
{
+ CFMutableDataRef data;
+ CFDictionaryRef attrs;
+ OSStatus result;
+
/* Only store complete credentials */
if (!protocol || !host || !username || !password)
- return;
+ return -1;
- if (SecKeychainAddInternetPassword(
- KEYCHAIN_ARGS,
- KEYCHAIN_ITEM(password),
- NULL))
- return;
+ data = CFDataCreateMutableCopy(kCFAllocatorDefault, 0, password);
+ if (password_expiry_utc) {
+ CFDataAppendBytes(data,
+ (const UInt8 *)STRING_WITH_LENGTH("\npassword_expiry_utc="));
+ CFDataAppendBytes(data,
+ CFDataGetBytePtr(password_expiry_utc),
+ CFDataGetLength(password_expiry_utc));
+ }
+ if (oauth_refresh_token) {
+ CFDataAppendBytes(data,
+ (const UInt8 *)STRING_WITH_LENGTH("\noauth_refresh_token="));
+ CFDataAppendBytes(data,
+ CFDataGetBytePtr(oauth_refresh_token),
+ CFDataGetLength(oauth_refresh_token));
+ }
+
+ attrs = CREATE_SEC_ATTRIBUTES(kSecValueData, data,
+ NULL);
+
+ result = SecItemAdd(attrs, NULL);
+ if (result == errSecDuplicateItem) {
+ CFDictionaryRef query;
+ query = CREATE_SEC_ATTRIBUTES(NULL);
+ result = SecItemUpdate(query, attrs);
+ CFRelease(query);
+ }
+
+ CFRelease(data);
+ CFRelease(attrs);
+
+ return result;
}
static void read_credential(void)
@@ -131,36 +341,60 @@ static void read_credential(void)
if (!strcmp(buf, "protocol")) {
if (!strcmp(v, "imap"))
- protocol = kSecProtocolTypeIMAP;
+ protocol = kSecAttrProtocolIMAP;
else if (!strcmp(v, "imaps"))
- protocol = kSecProtocolTypeIMAPS;
+ protocol = kSecAttrProtocolIMAPS;
else if (!strcmp(v, "ftp"))
- protocol = kSecProtocolTypeFTP;
+ protocol = kSecAttrProtocolFTP;
else if (!strcmp(v, "ftps"))
- protocol = kSecProtocolTypeFTPS;
+ protocol = kSecAttrProtocolFTPS;
else if (!strcmp(v, "https"))
- protocol = kSecProtocolTypeHTTPS;
+ protocol = kSecAttrProtocolHTTPS;
else if (!strcmp(v, "http"))
- protocol = kSecProtocolTypeHTTP;
+ protocol = kSecAttrProtocolHTTP;
else if (!strcmp(v, "smtp"))
- protocol = kSecProtocolTypeSMTP;
- else /* we don't yet handle other protocols */
+ protocol = kSecAttrProtocolSMTP;
+ else {
+ /* we don't yet handle other protocols */
+ clear_credential();
exit(0);
+ }
}
else if (!strcmp(buf, "host")) {
char *colon = strchr(v, ':');
if (colon) {
+ UInt16 port_i;
*colon++ = '\0';
- port = atoi(colon);
+ port_i = atoi(colon);
+ port = CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberShortType,
+ &port_i);
}
- host = xstrdup(v);
+ host = CFStringCreateWithCString(kCFAllocatorDefault,
+ v,
+ ENCODING);
}
else if (!strcmp(buf, "path"))
- path = xstrdup(v);
+ path = CFStringCreateWithCString(kCFAllocatorDefault,
+ v,
+ ENCODING);
else if (!strcmp(buf, "username"))
- username = xstrdup(v);
+ username = CFStringCreateWithCString(
+ kCFAllocatorDefault,
+ v,
+ ENCODING);
else if (!strcmp(buf, "password"))
- password = xstrdup(v);
+ password = CFDataCreate(kCFAllocatorDefault,
+ (UInt8 *)v,
+ strlen(v));
+ else if (!strcmp(buf, "password_expiry_utc"))
+ password_expiry_utc = CFDataCreate(kCFAllocatorDefault,
+ (UInt8 *)v,
+ strlen(v));
+ else if (!strcmp(buf, "oauth_refresh_token"))
+ oauth_refresh_token = CFDataCreate(kCFAllocatorDefault,
+ (UInt8 *)v,
+ strlen(v));
/*
* Ignore other lines; we don't know what they mean, but
* this future-proofs us when later versions of git do
@@ -173,6 +407,7 @@ static void read_credential(void)
int main(int argc, const char **argv)
{
+ OSStatus result = 0;
const char *usage =
"usage: git credential-osxkeychain <get|store|erase>";
@@ -182,12 +417,17 @@ int main(int argc, const char **argv)
read_credential();
if (!strcmp(argv[1], "get"))
- find_internet_password();
+ result = find_internet_password();
else if (!strcmp(argv[1], "store"))
- add_internet_password();
+ result = add_internet_password();
else if (!strcmp(argv[1], "erase"))
- delete_internet_password();
+ result = delete_internet_password();
/* otherwise, ignore unknown action */
+ if (result)
+ die("failed to %s: %d", argv[1], (int)result);
+
+ clear_credential();
+
return 0;
}
diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py
deleted file mode 100755
index 7eb1b24cc7..0000000000
--- a/contrib/hg-to-git/hg-to-git.py
+++ /dev/null
@@ -1,254 +0,0 @@
-#!/usr/bin/env python
-
-""" hg-to-git.py - A Mercurial to GIT converter
-
- Copyright (C)2007 Stelian Pop <stelian@popies.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, see <http://www.gnu.org/licenses/>.
-"""
-
-import os, os.path, sys
-import tempfile, pickle, getopt
-import re
-
-if sys.hexversion < 0x02030000:
- # The behavior of the pickle module changed significantly in 2.3
- sys.stderr.write("hg-to-git.py: requires Python 2.3 or later.\n")
- sys.exit(1)
-
-# Maps hg version -> git version
-hgvers = {}
-# List of children for each hg revision
-hgchildren = {}
-# List of parents for each hg revision
-hgparents = {}
-# Current branch for each hg revision
-hgbranch = {}
-# Number of new changesets converted from hg
-hgnewcsets = 0
-
-#------------------------------------------------------------------------------
-
-def usage():
-
- print("""\
-%s: [OPTIONS] <hgprj>
-
-options:
- -s, --gitstate=FILE: name of the state to be saved/read
- for incrementals
- -n, --nrepack=INT: number of changesets that will trigger
- a repack (default=0, -1 to deactivate)
- -v, --verbose: be verbose
-
-required:
- hgprj: name of the HG project to import (directory)
-""" % sys.argv[0])
-
-#------------------------------------------------------------------------------
-
-def getgitenv(user, date):
- env = ''
- elems = re.compile('(.*?)\s+<(.*)>').match(user)
- if elems:
- env += 'export GIT_AUTHOR_NAME="%s" ;' % elems.group(1)
- env += 'export GIT_COMMITTER_NAME="%s" ;' % elems.group(1)
- env += 'export GIT_AUTHOR_EMAIL="%s" ;' % elems.group(2)
- env += 'export GIT_COMMITTER_EMAIL="%s" ;' % elems.group(2)
- else:
- env += 'export GIT_AUTHOR_NAME="%s" ;' % user
- env += 'export GIT_COMMITTER_NAME="%s" ;' % user
- env += 'export GIT_AUTHOR_EMAIL= ;'
- env += 'export GIT_COMMITTER_EMAIL= ;'
-
- env += 'export GIT_AUTHOR_DATE="%s" ;' % date
- env += 'export GIT_COMMITTER_DATE="%s" ;' % date
- return env
-
-#------------------------------------------------------------------------------
-
-state = ''
-opt_nrepack = 0
-verbose = False
-
-try:
- opts, args = getopt.getopt(sys.argv[1:], 's:t:n:v', ['gitstate=', 'tempdir=', 'nrepack=', 'verbose'])
- for o, a in opts:
- if o in ('-s', '--gitstate'):
- state = a
- state = os.path.abspath(state)
- if o in ('-n', '--nrepack'):
- opt_nrepack = int(a)
- if o in ('-v', '--verbose'):
- verbose = True
- if len(args) != 1:
- raise Exception('params')
-except:
- usage()
- sys.exit(1)
-
-hgprj = args[0]
-os.chdir(hgprj)
-
-if state:
- if os.path.exists(state):
- if verbose:
- print('State does exist, reading')
- f = open(state, 'r')
- hgvers = pickle.load(f)
- else:
- print('State does not exist, first run')
-
-sock = os.popen('hg tip --template "{rev}"')
-tip = sock.read()
-if sock.close():
- sys.exit(1)
-if verbose:
- print('tip is', tip)
-
-# Calculate the branches
-if verbose:
- print('analysing the branches...')
-hgchildren["0"] = ()
-hgparents["0"] = (None, None)
-hgbranch["0"] = "master"
-for cset in range(1, int(tip) + 1):
- hgchildren[str(cset)] = ()
- prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().strip().split(' ')
- prnts = map(lambda x: x[:x.find(':')], prnts)
- if prnts[0] != '':
- parent = prnts[0].strip()
- else:
- parent = str(cset - 1)
- hgchildren[parent] += ( str(cset), )
- if len(prnts) > 1:
- mparent = prnts[1].strip()
- hgchildren[mparent] += ( str(cset), )
- else:
- mparent = None
-
- hgparents[str(cset)] = (parent, mparent)
-
- if mparent:
- # For merge changesets, take either one, preferably the 'master' branch
- if hgbranch[mparent] == 'master':
- hgbranch[str(cset)] = 'master'
- else:
- hgbranch[str(cset)] = hgbranch[parent]
- else:
- # Normal changesets
- # For first children, take the parent branch, for the others create a new branch
- if hgchildren[parent][0] == str(cset):
- hgbranch[str(cset)] = hgbranch[parent]
- else:
- hgbranch[str(cset)] = "branch-" + str(cset)
-
-if "0" not in hgvers:
- print('creating repository')
- os.system('git init')
-
-# loop through every hg changeset
-for cset in range(int(tip) + 1):
-
- # incremental, already seen
- if str(cset) in hgvers:
- continue
- hgnewcsets += 1
-
- # get info
- log_data = os.popen('hg log -r %d --template "{tags}\n{date|date}\n{author}\n"' % cset).readlines()
- tag = log_data[0].strip()
- date = log_data[1].strip()
- user = log_data[2].strip()
- parent = hgparents[str(cset)][0]
- mparent = hgparents[str(cset)][1]
-
- #get comment
- (fdcomment, filecomment) = tempfile.mkstemp()
- csetcomment = os.popen('hg log -r %d --template "{desc}"' % cset).read().strip()
- os.write(fdcomment, csetcomment)
- os.close(fdcomment)
-
- print('-----------------------------------------')
- print('cset:', cset)
- print('branch:', hgbranch[str(cset)])
- print('user:', user)
- print('date:', date)
- print('comment:', csetcomment)
- if parent:
- print('parent:', parent)
- if mparent:
- print('mparent:', mparent)
- if tag:
- print('tag:', tag)
- print('-----------------------------------------')
-
- # checkout the parent if necessary
- if cset != 0:
- if hgbranch[str(cset)] == "branch-" + str(cset):
- print('creating new branch', hgbranch[str(cset)])
- os.system('git checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent]))
- else:
- print('checking out branch', hgbranch[str(cset)])
- os.system('git checkout %s' % hgbranch[str(cset)])
-
- # merge
- if mparent:
- if hgbranch[parent] == hgbranch[str(cset)]:
- otherbranch = hgbranch[mparent]
- else:
- otherbranch = hgbranch[parent]
- print('merging', otherbranch, 'into', hgbranch[str(cset)])
- os.system(getgitenv(user, date) + 'git merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch))
-
- # remove everything except .git and .hg directories
- os.system('find . \( -path "./.hg" -o -path "./.git" \) -prune -o ! -name "." -print | xargs rm -rf')
-
- # repopulate with checkouted files
- os.system('hg update -C %d' % cset)
-
- # add new files
- os.system('git ls-files -x .hg --others | git update-index --add --stdin')
- # delete removed files
- os.system('git ls-files -x .hg --deleted | git update-index --remove --stdin')
-
- # commit
- os.system(getgitenv(user, date) + 'git commit --allow-empty --allow-empty-message -a -F %s' % filecomment)
- os.unlink(filecomment)
-
- # tag
- if tag and tag != 'tip':
- os.system(getgitenv(user, date) + 'git tag %s' % tag)
-
- # delete branch if not used anymore...
- if mparent and len(hgchildren[str(cset)]):
- print("Deleting unused branch:", otherbranch)
- os.system('git branch -d %s' % otherbranch)
-
- # retrieve and record the version
- vvv = os.popen('git show --quiet --pretty=format:%H').read()
- print('record', cset, '->', vvv)
- hgvers[str(cset)] = vvv
-
-if hgnewcsets >= opt_nrepack and opt_nrepack != -1:
- os.system('git repack -a -d')
-
-# write the state for incrementals
-if state:
- if verbose:
- print('Writing state')
- f = open(state, 'w')
- pickle.dump(hgvers, f)
-
-# vim: et ts=8 sw=4 sts=4
diff --git a/contrib/hg-to-git/hg-to-git.txt b/contrib/hg-to-git/hg-to-git.txt
deleted file mode 100644
index 91f8fe6410..0000000000
--- a/contrib/hg-to-git/hg-to-git.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-hg-to-git.py is able to convert a Mercurial repository into a git one,
-and preserves the branches in the process (unlike tailor)
-
-hg-to-git.py can probably be greatly improved (it's a rather crude
-combination of shell and python) but it does already work quite well for
-me. Features:
- - supports incremental conversion
- (for keeping a git repo in sync with a hg one)
- - supports hg branches
- - converts hg tags
-
-Note that the git repository will be created 'in place' (at the same
-location as the source hg repo). You will have to manually remove the
-'.hg' directory after the conversion.
-
-Also note that the incremental conversion uses 'simple' hg changesets
-identifiers (ordinals, as opposed to SHA-1 ids), and since these ids
-are not stable across different repositories the hg-to-git.py state file
-is forever tied to one hg repository.
-
-Stelian Pop <stelian@popies.net>
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index ca4df5be83..c3bd2a58b9 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -63,7 +63,7 @@ test_create_pre2_32_repo () {
git -C "$1" log -1 --format=%B HEAD^2 >msg &&
test_commit -C "$1-sub" --annotate sub2 &&
git clone --no-local "$1" "$1-clone" &&
- new_commit=$(cat msg | sed -e "s/$commit/$tag/" | git -C "$1-clone" commit-tree HEAD^2^{tree}) &&
+ new_commit=$(sed -e "s/$commit/$tag/" msg | git -C "$1-clone" commit-tree HEAD^2^{tree}) &&
git -C "$1-clone" replace HEAD^2 $new_commit
}
diff --git a/contrib/vscode/init.sh b/contrib/vscode/init.sh
index 521d303722..f2d61bb0e6 100755
--- a/contrib/vscode/init.sh
+++ b/contrib/vscode/init.sh
@@ -92,7 +92,6 @@ cat >.vscode/settings.json.new <<\EOF ||
"isexe",
"iskeychar",
"kompare",
- "mksnpath",
"mktag",
"mktree",
"mmblob",
diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir
index 888c34a521..989197aace 100755
--- a/contrib/workdir/git-new-workdir
+++ b/contrib/workdir/git-new-workdir
@@ -79,7 +79,7 @@ trap cleanup $siglist
# create the links to the original repo. explicitly exclude index, HEAD and
# logs/HEAD from the list since they are purely related to the current working
# directory, and should not be shared.
-for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn
+for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn reftable
do
# create a containing directory if needed
case $x in