diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..56725e7b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,306 @@ +# editorconfig.org + +# top-most EditorConfig file +root = true + +# Default settings: +# A newline ending every file +# Use 4 spaces as indentation +[*] +insert_final_newline = true +indent_style = space +indent_size = 4 +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_prefer_collection_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion +dotnet_style_allow_multiple_blank_lines_experimental = true:silent + +# C# files +[*.cs] +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# trim_trailing_whitespace = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current + +# avoid this. unless absolutely necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# prefer var +csharp_style_var_for_built_in_types = true +csharp_style_var_when_type_is_apparent = true +csharp_style_var_elsewhere = true:suggestion + +# use language keywords instead of BCL types +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# name all constant fields using PascalCase +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style + +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.required_modifiers = const + +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# private static fields should have s_ prefix +dotnet_naming_rule.private_static_fields_should_have_prefix.severity = suggestion +dotnet_naming_rule.private_static_fields_should_have_prefix.symbols = private_static_fields +dotnet_naming_rule.private_static_fields_should_have_prefix.style = private_static_prefix_style + +dotnet_naming_symbols.private_static_fields.applicable_kinds = field +dotnet_naming_symbols.private_static_fields.required_modifiers = static +dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private + +dotnet_naming_style.private_static_prefix_style.required_prefix = s_ +dotnet_naming_style.private_static_prefix_style.capitalization = camel_case + +# internal and private fields should be _camelCase +dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion +dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style + +dotnet_naming_symbols.private_internal_fields.applicable_kinds = field +dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal + +dotnet_naming_style.camel_case_underscore_style.required_prefix = _ +dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case + +# use accessibility modifiers +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +# Code style defaults +dotnet_sort_system_directives_first = true +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion + +# Expression-bodied members +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Pattern matching +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion + +# Null checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false +space_within_single_line_array_initializer_braces = true + +#Net Analyzer +dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomment when all violations are fixed. + +# CS0649: Field 'field' is never assigned to, and will always have its default value 'value' +dotnet_diagnostic.CS0649.severity = error + +# CS1591: Missing XML comment for publicly visible type or member +dotnet_diagnostic.CS1591.severity = suggestion + +# CS0162: Remove unreachable code +dotnet_diagnostic.CS0162.severity = error +# CA1018: Mark attributes with AttributeUsageAttribute +dotnet_diagnostic.CA1018.severity = error +# CA1304: Specify CultureInfo +dotnet_diagnostic.CA1304.severity = warning +# CA1802: Use literals where appropriate +dotnet_diagnostic.CA1802.severity = warning +# CA1813: Avoid unsealed attributes +dotnet_diagnostic.CA1813.severity = error +# CA1815: Override equals and operator equals on value types +dotnet_diagnostic.CA1815.severity = warning +# CA1820: Test for empty strings using string length +dotnet_diagnostic.CA1820.severity = warning +# CA1821: Remove empty finalizers +dotnet_diagnostic.CA1821.severity = warning +# CA1822: Mark members as static +dotnet_diagnostic.CA1822.severity = suggestion +# CA1823: Avoid unused private fields +dotnet_diagnostic.CA1823.severity = warning +dotnet_code_quality.CA1822.api_surface = private, internal +# CA1825: Avoid zero-length array allocations +dotnet_diagnostic.CA1825.severity = warning +# CA1826: Use property instead of Linq Enumerable method +dotnet_diagnostic.CA1826.severity = suggestion +# CA1827: Do not use Count/LongCount when Any can be used +dotnet_diagnostic.CA1827.severity = warning +# CA1828: Do not use CountAsync/LongCountAsync when AnyAsync can be used +dotnet_diagnostic.CA1828.severity = warning +# CA1829: Use Length/Count property instead of Enumerable.Count method +dotnet_diagnostic.CA1829.severity = warning +#CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters +dotnet_diagnostic.CA1847.severity = warning +#CA1854: Prefer the IDictionary.TryGetValue(TKey, out TValue) method +dotnet_diagnostic.CA1854.severity = warning +#CA2211:Non-constant fields should not be visible +dotnet_diagnostic.CA2211.severity = error + +# IDE0005: remove used namespace using +dotnet_diagnostic.IDE0005.severity = error + +# Wrapping preferences +csharp_wrap_before_ternary_opsigns = false + +# Avalonia DevAnalyzer preferences +dotnet_diagnostic.AVADEV2001.severity = error + +# Avalonia PublicAnalyzer preferences +dotnet_diagnostic.AVP1000.severity = error +dotnet_diagnostic.AVP1001.severity = error +dotnet_diagnostic.AVP1002.severity = error +dotnet_diagnostic.AVP1010.severity = error +dotnet_diagnostic.AVP1011.severity = error +dotnet_diagnostic.AVP1012.severity = warning +dotnet_diagnostic.AVP1013.severity = error +dotnet_diagnostic.AVP1020.severity = error +dotnet_diagnostic.AVP1021.severity = error +dotnet_diagnostic.AVP1022.severity = error +dotnet_diagnostic.AVP1030.severity = error +dotnet_diagnostic.AVP1031.severity = error +dotnet_diagnostic.AVP1032.severity = error +dotnet_diagnostic.AVP1040.severity = error +dotnet_diagnostic.AVA2001.severity = error +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_style_prefer_readonly_struct = true:suggestion +csharp_prefer_static_local_function = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion + +# Xaml files +[*.{xaml,axaml}] +indent_size = 2 +# DuplicateSetterError +avalonia_xaml_diagnostic.AVLN2203.severity = error +# StyleInMergedDictionaries +avalonia_xaml_diagnostic.AVLN2204.severity = error +# RequiredTemplatePartMissing +avalonia_xaml_diagnostic.AVLN2205.severity = error +# OptionalTemplatePartMissing +avalonia_xaml_diagnostic.AVLN2206.severity = info +# TemplatePartWrongType +avalonia_xaml_diagnostic.AVLN2207.severity = error +# Obsolete +avalonia_xaml_diagnostic.AVLN5001.severity = error + +# Xml project files +[*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] +indent_size = 2 + +# Xml build files +[*.builds] +indent_size = 2 + +# Xml files +[*.{xml,stylecop,resx,ruleset}] +indent_size = 2 + +# Xml config files +[*.{props,targets,config,nuspec}] +indent_size = 2 + +[*.json] +indent_size = 2 + +# Shell scripts +[*.sh] +end_of_line = lf +[*.{cmd,bat}] +end_of_line = crlf + +# Package manifests +[{*.spec,control}] +end_of_line = lf + +# YAML files +[*.{yml,yaml}] +indent_size = 2 +end_of_line = lf diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..bd1dfea9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,14 @@ +* text=auto +*.md text +*.png binary +*.ico binary +*.sh text eol=lf +*.spec text eol=lf +control text eol=lf +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf +*.json text + +.gitattributes export-ignore +.gitignore export-ignore diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..12792cf6 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,79 @@ +name: Build +on: + workflow_call: +jobs: + build: + strategy: + matrix: + include: + - name : Windows x64 + os: windows-2019 + runtime: win-x64 + - name : Windows ARM64 + os: windows-2019 + runtime: win-arm64 + - name : macOS (Intel) + os: macos-13 + runtime: osx-x64 + - name : macOS (Apple Silicon) + os: macos-latest + runtime: osx-arm64 + - name : Linux + os: ubuntu-latest + runtime: linux-x64 + container: ubuntu:20.04 + - name : Linux (arm64) + os: ubuntu-latest + runtime: linux-arm64 + container: ubuntu:20.04 + name: Build ${{ matrix.name }} + runs-on: ${{ matrix.os }} + container: ${{ matrix.container || '' }} + steps: + - name: Install common CLI tools + if: ${{ startsWith(matrix.runtime, 'linux-') }} + run: | + export DEBIAN_FRONTEND=noninteractive + ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime + apt-get update + apt-get install -y sudo + sudo apt-get install -y curl wget git unzip zip libicu66 tzdata clang + - name: Checkout sources + uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 9.0.x + - name: Configure arm64 packages + if: ${{ matrix.runtime == 'linux-arm64' }} + run: | + sudo dpkg --add-architecture arm64 + echo 'deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal main restricted + deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal-updates main restricted + deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ focal-backports main restricted' \ + | sudo tee /etc/apt/sources.list.d/arm64.list + sudo sed -i -e 's/^deb http/deb [arch=amd64] http/g' /etc/apt/sources.list + sudo sed -i -e 's/^deb mirror/deb [arch=amd64] mirror/g' /etc/apt/sources.list + - name: Install cross-compiling dependencies + if: ${{ matrix.runtime == 'linux-arm64' }} + run: | + sudo apt-get update + sudo apt-get install -y llvm gcc-aarch64-linux-gnu + - name: Build + run: dotnet build -c Release + - name: Publish + run: dotnet publish src/SourceGit.csproj -c Release -o publish -r ${{ matrix.runtime }} + - name: Rename executable file + if: ${{ startsWith(matrix.runtime, 'linux-') }} + run: mv publish/SourceGit publish/sourcegit + - name: Tar artifact + if: ${{ startsWith(matrix.runtime, 'linux-') || startsWith(matrix.runtime, 'osx-') }} + run: | + tar -cvf "sourcegit.${{ matrix.runtime }}.tar" -C publish . + rm -r publish/* + mv "sourcegit.${{ matrix.runtime }}.tar" publish + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: sourcegit.${{ matrix.runtime }} + path: publish/* diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..50e02dc9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: Continuous Integration +on: + push: + branches: [develop] + pull_request: + branches: [develop] + workflow_dispatch: + workflow_call: +jobs: + build: + name: Build + uses: ./.github/workflows/build.yml + version: + name: Prepare version string + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: Output version string + id: version + run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT" + package: + needs: [build, version] + name: Package + uses: ./.github/workflows/package.yml + with: + version: ${{ needs.version.outputs.version }} diff --git a/.github/workflows/localization-check.yml b/.github/workflows/localization-check.yml new file mode 100644 index 00000000..8dcd61c8 --- /dev/null +++ b/.github/workflows/localization-check.yml @@ -0,0 +1,41 @@ +name: Localization Check +on: + push: + branches: [ develop ] + paths: + - 'src/Resources/Locales/**' + workflow_dispatch: + workflow_call: + +jobs: + localization-check: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.x' + + - name: Install dependencies + run: npm install fs-extra@11.2.0 path@0.12.7 xml2js@0.6.2 + + - name: Run localization check + run: node build/scripts/localization-check.js + + - name: Commit changes + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + if [ -n "$(git status --porcelain)" ]; then + git add TRANSLATION.md src/Resources/Locales/*.axaml + git commit -m 'doc: Update translation status and sort locale files' + git push + else + echo "No changes to commit" + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml new file mode 100644 index 00000000..2dfc97fd --- /dev/null +++ b/.github/workflows/package.yml @@ -0,0 +1,111 @@ +name: Package +on: + workflow_call: + inputs: + version: + description: SourceGit package version + required: true + type: string +jobs: + windows: + name: Package Windows + runs-on: windows-2019 + strategy: + matrix: + runtime: [ win-x64, win-arm64 ] + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: Download build + uses: actions/download-artifact@v4 + with: + name: sourcegit.${{ matrix.runtime }} + path: build/SourceGit + - name: Package + shell: bash + env: + VERSION: ${{ inputs.version }} + RUNTIME: ${{ matrix.runtime }} + run: ./build/scripts/package.windows.sh + - name: Upload package artifact + uses: actions/upload-artifact@v4 + with: + name: package.${{ matrix.runtime }} + path: build/sourcegit_*.zip + - name: Delete temp artifacts + uses: geekyeggo/delete-artifact@v5 + with: + name: sourcegit.${{ matrix.runtime }} + osx-app: + name: Package macOS + runs-on: macos-latest + strategy: + matrix: + runtime: [osx-x64, osx-arm64] + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: Download build + uses: actions/download-artifact@v4 + with: + name: sourcegit.${{ matrix.runtime }} + path: build + - name: Package + env: + VERSION: ${{ inputs.version }} + RUNTIME: ${{ matrix.runtime }} + run: | + mkdir build/SourceGit + tar -xf "build/sourcegit.${{ matrix.runtime }}.tar" -C build/SourceGit + ./build/scripts/package.osx-app.sh + - name: Upload package artifact + uses: actions/upload-artifact@v4 + with: + name: package.${{ matrix.runtime }} + path: build/sourcegit_*.zip + - name: Delete temp artifacts + uses: geekyeggo/delete-artifact@v5 + with: + name: sourcegit.${{ matrix.runtime }} + linux: + name: Package Linux + runs-on: ubuntu-latest + container: ubuntu:20.04 + strategy: + matrix: + runtime: [linux-x64, linux-arm64] + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: Download package dependencies + run: | + export DEBIAN_FRONTEND=noninteractive + ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime + apt-get update + apt-get install -y curl wget git dpkg-dev fakeroot tzdata zip unzip desktop-file-utils rpm libfuse2 file build-essential binutils + - name: Download build + uses: actions/download-artifact@v4 + with: + name: sourcegit.${{ matrix.runtime }} + path: build + - name: Package + env: + VERSION: ${{ inputs.version }} + RUNTIME: ${{ matrix.runtime }} + APPIMAGE_EXTRACT_AND_RUN: 1 + run: | + mkdir build/SourceGit + tar -xf "build/sourcegit.${{ matrix.runtime }}.tar" -C build/SourceGit + ./build/scripts/package.linux.sh + - name: Upload package artifacts + uses: actions/upload-artifact@v4 + with: + name: package.${{ matrix.runtime }} + path: | + build/sourcegit-*.AppImage + build/sourcegit_*.deb + build/sourcegit-*.rpm + - name: Delete temp artifacts + uses: geekyeggo/delete-artifact@v5 + with: + name: sourcegit.${{ matrix.runtime }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..e61e608b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,52 @@ +name: Release +on: + push: + tags: + - v* +jobs: + build: + name: Build + uses: ./.github/workflows/build.yml + version: + name: Prepare version string + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - name: Output version string + id: version + env: + TAG: ${{ github.ref_name }} + run: echo "version=${TAG#v}" >> "$GITHUB_OUTPUT" + package: + needs: [build, version] + name: Package + uses: ./.github/workflows/package.yml + with: + version: ${{ needs.version.outputs.version }} + release: + needs: [package, version] + name: Release + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: Create release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.ref_name }} + VERSION: ${{ needs.version.outputs.version }} + run: gh release create "$TAG" -t "$VERSION" --notes-from-tag + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + pattern: package.* + path: packages + merge-multiple: true + - name: Upload assets + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.ref_name }} + run: gh release upload "$TAG" packages/* diff --git a/.gitignore b/.gitignore index 725330cd..e686a534 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,41 @@ -.idea -.vs -bin -obj \ No newline at end of file +.vs/ +.vscode/ +.idea/ + +*.sln.docstates +*.user +*.suo +*.code-workspace + +.DS_Store +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +bin/ +obj/ +# ignore ci node files +node_modules/ +package.json +package-lock.json + +build/resources/ +build/SourceGit/ +build/SourceGit.app/ +build/*.zip +build/*.tar.gz +build/*.deb +build/*.rpm +build/*.AppImage +SourceGit.app/ +build.command +src/Properties/launchSettings.json diff --git a/LICENSE b/LICENSE index 05cd0620..442ce085 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2018 leo +Copyright (c) 2025 sourcegit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in @@ -17,4 +17,4 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Preview_Dark.png b/Preview_Dark.png deleted file mode 100644 index 43ef67f9..00000000 Binary files a/Preview_Dark.png and /dev/null differ diff --git a/Preview_Light.png b/Preview_Light.png deleted file mode 100644 index 09792474..00000000 Binary files a/Preview_Light.png and /dev/null differ diff --git a/README.md b/README.md index c0fd512f..f9ba3072 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,207 @@ -# SourceGit +# SourceGit - Opensource Git GUI client. -开源的Git客户端,仅用于Windows 10。单文件,无需安装,< 500KB。 +[![stars](https://img.shields.io/github/stars/sourcegit-scm/sourcegit.svg)](https://github.com/sourcegit-scm/sourcegit/stargazers) +[![forks](https://img.shields.io/github/forks/sourcegit-scm/sourcegit.svg)](https://github.com/sourcegit-scm/sourcegit/forks) +[![license](https://img.shields.io/github/license/sourcegit-scm/sourcegit.svg)](LICENSE) +[![latest](https://img.shields.io/github/v/release/sourcegit-scm/sourcegit.svg)](https://github.com/sourcegit-scm/sourcegit/releases/latest) +[![downloads](https://img.shields.io/github/downloads/sourcegit-scm/sourcegit/total)](https://github.com/sourcegit-scm/sourcegit/releases) -## 预览 +## Highlights -* DarkTheme +* Supports Windows/macOS/Linux +* Opensource/Free +* Fast +* Deutsch/English/Español/Français/Italiano/Português/Русский/Українська/简体中文/繁體中文/日本語/தமிழ் (Tamil) +* Built-in light/dark themes +* Customize theme +* Visual commit graph +* Supports SSH access with each remote +* GIT commands with GUI + * Clone/Fetch/Pull/Push... + * Merge/Rebase/Reset/Revert/Cherry-pick... + * Amend/Reword/Squash + * Interactive rebase + * Branches + * Remotes + * Tags + * Stashes + * Submodules + * Worktrees + * Archive + * Diff + * Save as patch/apply + * File histories + * Blame + * Revision Diffs + * Branch Diff + * Image Diff - Side-By-Side/Swipe/Blend +* Git command logs +* Search commits +* GitFlow +* Git LFS +* Bisect +* Issue Link +* Workspace +* Custom Action +* Using AI to generate commit message (C# port of [anjerodev/commitollama](https://github.com/anjerodev/commitollama)) -![Preview_Dark](./Preview_Dark.png) +> [!WARNING] +> **Linux** only tested on **Debian 12** on both **X11** & **Wayland**. -* LightTheme +## Translation Status -![Preview_Light](./Preview_Light.png) +You can find the current translation status in [TRANSLATION.md](https://github.com/sourcegit-scm/sourcegit/blob/develop/TRANSLATION.md) +## How to Use -## Thanks +**To use this tool, you need to install Git(>=2.25.1) first.** -* [PUMA](https://gitee.com/whgfu) 配置默认User +You can download the latest stable from [Releases](https://github.com/sourcegit-scm/sourcegit/releases/latest) or download workflow artifacts from [GitHub Actions](https://github.com/sourcegit-scm/sourcegit/actions) to try this app based on latest commits. + +This software creates a folder `$"{System.Environment.SpecialFolder.ApplicationData}/SourceGit"`, which is platform-dependent, to store user settings, downloaded avatars and crash logs. + +| OS | PATH | +|---------|-----------------------------------------------------| +| Windows | `%APPDATA%\SourceGit` | +| Linux | `${HOME}/.config/SourceGit` or `${HOME}/.sourcegit` | +| macOS | `${HOME}/Library/Application Support/SourceGit` | + +> [!TIP] +> * You can open this data storage directory from the main menu `Open Data Storage Directory`. +> * You can create a `data` folder next to the `SourceGit` executable to force this app to store data (user settings, downloaded avatars and crash logs) into it (Portable-Mode). Only works on Windows. + +For **Windows** users: + +* **MSYS Git is NOT supported**. Please use official [Git for Windows](https://git-scm.com/download/win) instead. +* You can install the latest stable from `winget` with follow commands: + ```shell + winget install SourceGit + ``` +> [!NOTE] +> `winget` will install this software as a commandline tool. You need run `SourceGit` from console or `Win+R` at the first time. Then you can add it to the taskbar. +* You can install the latest stable by `scoop` with follow commands: + ```shell + scoop bucket add extras + scoop install sourcegit + ``` +* Pre-built binaries can be found in [Releases](https://github.com/sourcegit-scm/sourcegit/releases/latest) + +For **macOS** users: + +* Thanks [@ybeapps](https://github.com/ybeapps) for making `SourceGit` available on `Homebrew`. You can simply install it with following command: + ```shell + brew tap ybeapps/homebrew-sourcegit + brew install --cask --no-quarantine sourcegit + ``` +* If you want to install `SourceGit.app` from GitHub Release manually, you need run following command to make sure it works: + ```shell + sudo xattr -cr /Applications/SourceGit.app + ``` +* Make sure [git-credential-manager](https://github.com/git-ecosystem/git-credential-manager/releases) is installed on your mac. +* You can run `echo $PATH > ~/Library/Application\ Support/SourceGit/PATH` to generate a custom PATH env file to introduce `PATH` env to SourceGit. + +For **Linux** users: + +* Thanks [@aikawayataro](https://github.com/aikawayataro) for providing `rpm` and `deb` repositories, hosted on [Codeberg](https://codeberg.org/yataro/-/packages). + + `deb` how to: + ```shell + curl https://codeberg.org/api/packages/yataro/debian/repository.key | sudo tee /etc/apt/keyrings/sourcegit.asc + echo "deb [signed-by=/etc/apt/keyrings/sourcegit.asc, arch=amd64,arm64] https://codeberg.org/api/packages/yataro/debian generic main" | sudo tee /etc/apt/sources.list.d/sourcegit.list + sudo apt update + sudo apt install sourcegit + ``` + + `rpm` how to: + ```shell + curl https://codeberg.org/api/packages/yataro/rpm.repo | sed -e 's/gpgcheck=1/gpgcheck=0/' > sourcegit.repo + + # Fedora 41 and newer + sudo dnf config-manager addrepo --from-repofile=./sourcegit.repo + # Fedora 40 and earlier + sudo dnf config-manager --add-repo ./sourcegit.repo + + sudo dnf install sourcegit + ``` + + If your distribution isn't using `dnf`, please refer to the documentation of your distribution on how to add an `rpm` repository. +* `AppImage` files can be found on [AppImage hub](https://appimage.github.io/SourceGit/), `xdg-open` (`xdg-utils`) must be installed to support open native file manager. +* Make sure [git-credential-manager](https://github.com/git-ecosystem/git-credential-manager/releases) is installed on your Linux. +* Maybe you need to set environment variable `AVALONIA_SCREEN_SCALE_FACTORS`. See https://github.com/AvaloniaUI/Avalonia/wiki/Configuring-X11-per-monitor-DPI. +* If you can NOT type accented characters, such as `ê`, `ó`, try to set the environment variable `AVALONIA_IM_MODULE` to `none`. + +## OpenAI + +This software supports using OpenAI or other AI service that has an OpenAI compatible HTTP API to generate commit message. You need configurate the service in `Preference` window. + +For `OpenAI`: + +* `Server` must be `https://api.openai.com/v1` + +For other AI service: + +* The `Server` should fill in a URL equivalent to OpenAI's `https://api.openai.com/v1`. For example, when using `Ollama`, it should be `http://localhost:11434/v1` instead of `http://localhost:11434/api/generate` +* The `API Key` is optional that depends on the service + +## External Tools + +This app supports open repository in external tools listed in the table below. + +| Tool | Windows | macOS | Linux | +|-------------------------------|---------|-------|-------| +| Visual Studio Code | YES | YES | YES | +| Visual Studio Code - Insiders | YES | YES | YES | +| VSCodium | YES | YES | YES | +| Fleet | YES | YES | YES | +| Sublime Text | YES | YES | YES | +| Zed | NO | YES | YES | +| Visual Studio | YES | NO | NO | + +> [!NOTE] +> This app will try to find those tools based on some pre-defined or expected locations automatically. If you are using one portable version of these tools, it will not be detected by this app. +> To solve this problem you can add a file named `external_editors.json` in app data storage directory and provide the path directly. For example: +```json +{ + "tools": { + "Visual Studio Code": "D:\\VSCode\\Code.exe" + } +} +``` + +> [!NOTE] +> This app also supports a lot of `JetBrains` IDEs, installing `JetBrains Toolbox` will help this app to find them. + +## Screenshots + +* Dark Theme + + ![Theme Dark](./screenshots/theme_dark.png) + +* Light Theme + + ![Theme Light](./screenshots/theme_light.png) + +* Custom + + You can find custom themes from [sourcegit-theme](https://github.com/sourcegit-scm/sourcegit-theme.git). And welcome to share your own themes. + +## Contributing + +Everyone is welcome to submit a PR. Please make sure your PR is based on the latest `develop` branch and the target branch of PR is `develop`. + +In short, here are the commands to get started once [.NET tools are installed](https://dotnet.microsoft.com/en-us/download): + +```sh +dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org +dotnet restore +dotnet build +dotnet run --project src/SourceGit.csproj +``` + +Thanks to all the people who contribute. + +[![Contributors](https://contrib.rocks/image?repo=sourcegit-scm/sourcegit&columns=20)](https://github.com/sourcegit-scm/sourcegit/graphs/contributors) + +## Third-Party Components + +For detailed license information, see [THIRD-PARTY-LICENSES.md](THIRD-PARTY-LICENSES.md). diff --git a/SourceGit.sln b/SourceGit.sln index 50aba2bc..624322f8 100644 --- a/SourceGit.sln +++ b/SourceGit.sln @@ -1,9 +1,89 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30011.22 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34714.143 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGit", "SourceGit\SourceGit.csproj", "{0A04DD59-7A6C-410C-B427-7DC8183993BD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SourceGit", "src\SourceGit.csproj", "{2091C34D-4A17-4375-BEF3-4D60BE8113E4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{773082AC-D9C8-4186-8521-4B6A7BEE6158}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "resources", "resources", "{FD384607-ED99-47B7-AF31-FB245841BC92}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{F45A9D95-AF25-42D8-BBAC-8259C9EEE820}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{67B6D05F-A000-40BA-ADB4-C9065F880D7B}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build.yml = .github\workflows\build.yml + .github\workflows\ci.yml = .github\workflows\ci.yml + .github\workflows\package.yml = .github\workflows\package.yml + .github\workflows\release.yml = .github\workflows\release.yml + .github\workflows\localization-check.yml = .github\workflows\localization-check.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{49A7C2D6-558C-4FAA-8F5D-EEE81497AED7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "files", "files", "{3AB707DB-A02C-4AFC-BF12-D7DF2B333BAC}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitattributes = .gitattributes + .gitignore = .gitignore + global.json = global.json + LICENSE = LICENSE + README.md = README.md + VERSION = VERSION + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "app", "app", "{ABC98884-F023-4EF4-A9C9-5DE9452BE955}" + ProjectSection(SolutionItems) = preProject + build\resources\app\App.icns = build\resources\app\App.icns + build\resources\app\App.plist = build\resources\app\App.plist + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_common", "_common", "{04FD74B1-FBDB-496E-A48F-3D59D71FF952}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "usr", "usr", "{76639799-54BC-45E8-BD90-F45F63ACD11D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "share", "share", "{A3ABAA7C-EE14-4448-B466-6E69C1347E7D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "applications", "applications", "{2AF28D3B-14A8-46A8-B828-157FAAB1B06F}" + ProjectSection(SolutionItems) = preProject + build\resources\_common\usr\share\applications\sourcegit.desktop = build\resources\_common\usr\share\applications\sourcegit.desktop + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "icons", "icons", "{7166EC6C-17F5-4B5E-B38E-1E53C81EACF6}" + ProjectSection(SolutionItems) = preProject + build\resources\_common\usr\share\icons\sourcegit.png = build\resources\_common\usr\share\icons\sourcegit.png + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deb", "deb", "{9C2F0CDA-B56E-44A5-94B6-F3EA7AC20CDC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DEBIAN", "DEBIAN", "{F101849D-BDB7-40D4-A516-751150C3CCFC}" + ProjectSection(SolutionItems) = preProject + build\resources\deb\DEBIAN\control = build\resources\deb\DEBIAN\control + build\resources\deb\DEBIAN\preinst = build\resources\deb\DEBIAN\preinst + build\resources\deb\DEBIAN\prerm = build\resources\deb\DEBIAN\prerm + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "rpm", "rpm", "{9BA0B044-0CC9-46F8-B551-204F149BF45D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SPECS", "SPECS", "{7802CD7A-591B-4EDD-96F8-9BF3F61692E4}" + ProjectSection(SolutionItems) = preProject + build\resources\rpm\SPECS\build.spec = build\resources\rpm\SPECS\build.spec + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "appimage", "appimage", "{5D125DD9-B48A-491F-B2FB-D7830D74C4DC}" + ProjectSection(SolutionItems) = preProject + build\resources\appimage\sourcegit.appdata.xml = build\resources\appimage\sourcegit.appdata.xml + build\resources\appimage\sourcegit.png = build\resources\appimage\sourcegit.png + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{C54D4001-9940-477C-A0B6-E795ED0A3209}" + ProjectSection(SolutionItems) = preProject + build\scripts\localization-check.js = build\scripts\localization-check.js + build\scripts\package.linux.sh = build\scripts\package.linux.sh + build\scripts\package.osx-app.sh = build\scripts\package.osx-app.sh + build\scripts\package.windows.sh = build\scripts\package.windows.sh + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,15 +91,32 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0A04DD59-7A6C-410C-B427-7DC8183993BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0A04DD59-7A6C-410C-B427-7DC8183993BD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0A04DD59-7A6C-410C-B427-7DC8183993BD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0A04DD59-7A6C-410C-B427-7DC8183993BD}.Release|Any CPU.Build.0 = Release|Any CPU + {2091C34D-4A17-4375-BEF3-4D60BE8113E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2091C34D-4A17-4375-BEF3-4D60BE8113E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2091C34D-4A17-4375-BEF3-4D60BE8113E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2091C34D-4A17-4375-BEF3-4D60BE8113E4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {2091C34D-4A17-4375-BEF3-4D60BE8113E4} = {49A7C2D6-558C-4FAA-8F5D-EEE81497AED7} + {FD384607-ED99-47B7-AF31-FB245841BC92} = {773082AC-D9C8-4186-8521-4B6A7BEE6158} + {67B6D05F-A000-40BA-ADB4-C9065F880D7B} = {F45A9D95-AF25-42D8-BBAC-8259C9EEE820} + {ABC98884-F023-4EF4-A9C9-5DE9452BE955} = {FD384607-ED99-47B7-AF31-FB245841BC92} + {04FD74B1-FBDB-496E-A48F-3D59D71FF952} = {FD384607-ED99-47B7-AF31-FB245841BC92} + {76639799-54BC-45E8-BD90-F45F63ACD11D} = {04FD74B1-FBDB-496E-A48F-3D59D71FF952} + {A3ABAA7C-EE14-4448-B466-6E69C1347E7D} = {76639799-54BC-45E8-BD90-F45F63ACD11D} + {2AF28D3B-14A8-46A8-B828-157FAAB1B06F} = {A3ABAA7C-EE14-4448-B466-6E69C1347E7D} + {7166EC6C-17F5-4B5E-B38E-1E53C81EACF6} = {A3ABAA7C-EE14-4448-B466-6E69C1347E7D} + {9C2F0CDA-B56E-44A5-94B6-F3EA7AC20CDC} = {FD384607-ED99-47B7-AF31-FB245841BC92} + {F101849D-BDB7-40D4-A516-751150C3CCFC} = {9C2F0CDA-B56E-44A5-94B6-F3EA7AC20CDC} + {9BA0B044-0CC9-46F8-B551-204F149BF45D} = {FD384607-ED99-47B7-AF31-FB245841BC92} + {7802CD7A-591B-4EDD-96F8-9BF3F61692E4} = {9BA0B044-0CC9-46F8-B551-204F149BF45D} + {5D125DD9-B48A-491F-B2FB-D7830D74C4DC} = {FD384607-ED99-47B7-AF31-FB245841BC92} + {C54D4001-9940-477C-A0B6-E795ED0A3209} = {773082AC-D9C8-4186-8521-4B6A7BEE6158} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {01F4EC04-5B3C-4D74-BB48-1C251B2D2853} + SolutionGuid = {7FF1B9C6-B5BF-4A50-949F-4B407A0E31C9} EndGlobalSection EndGlobal diff --git a/SourceGit/App.config b/SourceGit/App.config deleted file mode 100644 index 941cc663..00000000 --- a/SourceGit/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/SourceGit/App.ico b/SourceGit/App.ico deleted file mode 100644 index 9063ffee..00000000 Binary files a/SourceGit/App.ico and /dev/null differ diff --git a/SourceGit/App.manifest b/SourceGit/App.manifest deleted file mode 100644 index 5a6db8f3..00000000 --- a/SourceGit/App.manifest +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - PerMonitorV2 - true - - - diff --git a/SourceGit/App.xaml b/SourceGit/App.xaml deleted file mode 100644 index 408df2f0..00000000 --- a/SourceGit/App.xaml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/SourceGit/App.xaml.cs b/SourceGit/App.xaml.cs deleted file mode 100644 index fed0ffda..00000000 --- a/SourceGit/App.xaml.cs +++ /dev/null @@ -1,129 +0,0 @@ -using Microsoft.Win32; -using System; -using System.IO; -using System.Windows; - -namespace SourceGit { - - /// - /// Application. - /// - public partial class App : Application { - - /// - /// Getter/Setter for Git preference. - /// - public static Git.Preference Preference { - get { return Git.Preference.Instance; } - set { Git.Preference.Instance = value; } - } - - /// - /// Check if GIT has been configured. - /// - public static bool IsGitConfigured { - get { - return !string.IsNullOrEmpty(Preference.GitExecutable) - && File.Exists(Preference.GitExecutable); - } - } - - /// - /// Error handler. - /// - public static Action OnError { - get; - set; - } - - /// - /// Raise error message. - /// - /// - public static void RaiseError(string message) { - OnError?.Invoke(message); - } - - /// - /// Get popup manager by repository - /// - /// - /// - public static UI.PopupManager GetPopupManager(Git.Repository repo) { - var main = Current.MainWindow as UI.Launcher; - if (main == null) return null; - if (repo == null) return (main.Tabs[0].Page as UI.Manager).popupManager; - - for (int i = 1; i < main.openedTabs.Items.Count; i++) { - var opened = main.openedTabs.Items[i] as UI.Launcher.Tab; - if (opened.Repo.Path == repo.Path) { - return (opened.Page as UI.Dashboard).popupManager; - } - } - - return null; - } - - /// - /// Startup event. - /// - /// - /// - private void OnAppStartup(object sender, StartupEventArgs e) { - // Use this app as a sequence editor? - var args = e.Args; - if (args.Length > 1) { - if (args[0] == "--interactive-rebase") { - if (args.Length < 3) { - Environment.Exit(1); - return; - } - - File.WriteAllText(args[2], File.ReadAllText(args[1])); - } - - Environment.Exit(0); - return; - } - - // Try auto configure git via registry. - if (!IsGitConfigured) { - var root = RegistryKey.OpenBaseKey( - RegistryHive.LocalMachine, - Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); - - var git = root.OpenSubKey("SOFTWARE\\GitForWindows"); - if (git != null) { - Preference.GitExecutable = Path.Combine( - git.GetValue("InstallPath") as string, - "bin", - "git.exe"); - } - } - - // Apply themes - if (Preference.UIUseLightTheme) { - foreach (var rs in Current.Resources.MergedDictionaries) { - if (rs.Source != null && rs.Source.OriginalString.StartsWith("pack://application:,,,/Resources/Themes/")) { - rs.Source = new Uri("pack://application:,,,/Resources/Themes/Light.xaml", UriKind.Absolute); - break; - } - } - } - - // Show main window - Current.MainWindow = new UI.Launcher(); - Current.MainWindow.Show(); - } - - /// - /// Deactivated event. - /// - /// - /// - private void OnAppDeactivated(object sender, EventArgs e) { - Git.Preference.Save(); - GC.Collect(); - } - } -} diff --git a/SourceGit/Converters/BoolToCollapsed.cs b/SourceGit/Converters/BoolToCollapsed.cs deleted file mode 100644 index 47ce3c73..00000000 --- a/SourceGit/Converters/BoolToCollapsed.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Globalization; -using System.Windows; -using System.Windows.Data; - -namespace SourceGit.Converters { - - /// - /// Same as BoolToVisibilityConverter. - /// - public class BoolToCollapsed : IValueConverter { - - /// - /// Implement IValueConverter.Convert - /// - /// - /// - /// - /// - /// - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - return (bool)value ? Visibility.Visible : Visibility.Collapsed; - } - - /// - /// Implement IValueConverter.ConvertBack - /// - /// - /// - /// - /// - /// - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - throw new NotImplementedException(); - } - } -} diff --git a/SourceGit/Converters/FileStatusToColor.cs b/SourceGit/Converters/FileStatusToColor.cs deleted file mode 100644 index aba855f9..00000000 --- a/SourceGit/Converters/FileStatusToColor.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; -using System.Windows.Media; - -namespace SourceGit.Converters { - - /// - /// Convert file status to brush - /// - public class FileStatusToColor : IValueConverter { - - /// - /// Is only test local changes. - /// - public bool OnlyWorkTree { get; set; } = false; - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - var change = value as Git.Change; - if (change == null) return Brushes.Transparent; - - var status = Git.Change.Status.None; - if (OnlyWorkTree) { - if (change.IsConflit) return Brushes.Yellow; - status = change.WorkTree; - } else { - status = change.Index; - } - - if (App.Preference.UIUseLightTheme) { - switch (status) { - case Git.Change.Status.Modified: return Brushes.Goldenrod; - case Git.Change.Status.Added: return Brushes.Green; - case Git.Change.Status.Deleted: return Brushes.Red; - case Git.Change.Status.Renamed: return Brushes.Magenta; - case Git.Change.Status.Copied: return Brushes.Goldenrod; - case Git.Change.Status.Unmerged: return Brushes.Goldenrod; - case Git.Change.Status.Untracked: return Brushes.Green; - default: return Brushes.Transparent; - } - } else { - switch (status) { - case Git.Change.Status.Modified: return Brushes.DarkGoldenrod; - case Git.Change.Status.Added: return Brushes.DarkGreen; - case Git.Change.Status.Deleted: return Brushes.DarkRed; - case Git.Change.Status.Renamed: return Brushes.DarkMagenta; - case Git.Change.Status.Copied: return Brushes.DarkGoldenrod; - case Git.Change.Status.Unmerged: return Brushes.DarkGoldenrod; - case Git.Change.Status.Untracked: return Brushes.DarkGreen; - default: return Brushes.Transparent; - } - } - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - throw new NotImplementedException(); - } - } -} diff --git a/SourceGit/Converters/FileStatusToIcon.cs b/SourceGit/Converters/FileStatusToIcon.cs deleted file mode 100644 index 85447770..00000000 --- a/SourceGit/Converters/FileStatusToIcon.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; - -namespace SourceGit.Converters { - - /// - /// Convert file status to icon. - /// - public class FileStatusToIcon : IValueConverter { - - /// - /// Is only test local changes. - /// - public bool OnlyWorkTree { get; set; } = false; - - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - var change = value as Git.Change; - if (change == null) return ""; - - var status = Git.Change.Status.None; - if (OnlyWorkTree) { - if (change.IsConflit) return "X"; - status = change.WorkTree; - } else { - status = change.Index; - } - - switch (status) { - case Git.Change.Status.Modified: return "M"; - case Git.Change.Status.Added: return "A"; - case Git.Change.Status.Deleted: return "D"; - case Git.Change.Status.Renamed: return "R"; - case Git.Change.Status.Copied: return "C"; - case Git.Change.Status.Unmerged: return "U"; - case Git.Change.Status.Untracked: return "?"; - default: return "?"; - } - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - throw new NotImplementedException(); - } - } -} diff --git a/SourceGit/Converters/IndentToMargin.cs b/SourceGit/Converters/IndentToMargin.cs deleted file mode 100644 index 5214396b..00000000 --- a/SourceGit/Converters/IndentToMargin.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Globalization; -using System.Windows; -using System.Windows.Data; - -namespace SourceGit.Converters { - - /// - /// Convert indent(horizontal offset) to Margin property - /// - public class IndentToMargin : IValueConverter { - - /// - /// Implement IValueConverter.Convert - /// - /// - /// - /// - /// - /// - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - return new Thickness((double)value, 0, 0, 0); - } - - /// - /// Implement IValueConverter.ConvertBack - /// - /// - /// - /// - /// - /// - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - return ((Thickness)value).Left; - } - } -} diff --git a/SourceGit/Converters/InverseBool.cs b/SourceGit/Converters/InverseBool.cs deleted file mode 100644 index 932ae4ef..00000000 --- a/SourceGit/Converters/InverseBool.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; - -namespace SourceGit.Converters { - - /// - /// Inverse bool converter. - /// - public class InverseBool : IValueConverter { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - return !((bool)value); - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - throw new NotImplementedException(); - } - } -} diff --git a/SourceGit/Converters/InverseBoolToCollapsed.cs b/SourceGit/Converters/InverseBoolToCollapsed.cs deleted file mode 100644 index 862bf913..00000000 --- a/SourceGit/Converters/InverseBoolToCollapsed.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Globalization; -using System.Windows; -using System.Windows.Data; - -namespace SourceGit.Converters { - - /// - /// Inverse BoolToCollapsed. - /// - public class InverseBoolToCollapsed : IValueConverter { - - /// - /// Implement IValueConverter.Convert - /// - /// - /// - /// - /// - /// - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - return (bool)value ? Visibility.Collapsed : Visibility.Visible; - } - - /// - /// Implement IValueConverter.ConvertBack - /// - /// - /// - /// - /// - /// - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - throw new NotImplementedException(); - } - } -} diff --git a/SourceGit/Converters/PercentToDouble.cs b/SourceGit/Converters/PercentToDouble.cs deleted file mode 100644 index a76c9b92..00000000 --- a/SourceGit/Converters/PercentToDouble.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; - -namespace SourceGit.Converters { - - /// - /// Convert percent to double. - /// - public class PercentToDouble : IValueConverter { - - /// - /// Percentage. - /// - public double Percent { get; set; } - - /// - /// Implement IValueConverter.Convert - /// - /// - /// - /// - /// - /// - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - return (double)value * Percent; - } - - /// - /// Implement IValueConverter.ConvertBack - /// - /// - /// - /// - /// - /// - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - throw new NotImplementedException(); - } - } -} diff --git a/SourceGit/Converters/TreeViewItemDepthToMargin.cs b/SourceGit/Converters/TreeViewItemDepthToMargin.cs deleted file mode 100644 index 8c7eb856..00000000 --- a/SourceGit/Converters/TreeViewItemDepthToMargin.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Globalization; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Media; - -namespace SourceGit.Converters { - - /// - /// Convert depth of a TreeViewItem to Margin property. - /// - public class TreeViewItemDepthToMargin : IValueConverter { - - /// - /// Indent length - /// - public double Indent { get; set; } = 19; - - /// - /// Implement IValueConverter.Convert - /// - /// - /// - /// - /// - /// - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - TreeViewItem item = value as TreeViewItem; - if (item == null) return new Thickness(0); - - TreeViewItem iterator = GetParent(item); - int depth = 0; - while (iterator != null) { - depth++; - iterator = GetParent(iterator); - } - - return new Thickness(Indent * depth, 0, 0, 0); - } - - /// - /// Implement IValueConvert.ConvertBack - /// - /// - /// - /// - /// - /// - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - throw new NotImplementedException(); - } - - /// - /// Get parent item. - /// - /// - /// - private TreeViewItem GetParent(TreeViewItem item) { - var parent = VisualTreeHelper.GetParent(item); - - while (parent != null && !(parent is TreeView) && !(parent is TreeViewItem)) { - parent = VisualTreeHelper.GetParent(parent); - } - - return parent as TreeViewItem; - } - } -} diff --git a/SourceGit/Git/Blame.cs b/SourceGit/Git/Blame.cs deleted file mode 100644 index bf597274..00000000 --- a/SourceGit/Git/Blame.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; - -namespace SourceGit.Git { - - /// - /// Blame - /// - public class Blame { - - /// - /// Block content. - /// - public class Block { - public string CommitSHA { get; set; } - public string Author { get; set; } - public string Time { get; set; } - public string Content { get; set; } - } - - /// - /// Blocks - /// - public List Blocks { get; set; } = new List(); - - /// - /// Is binary file? - /// - public bool IsBinary { get; set; } = false; - - /// - /// Line count. - /// - public int LineCount { get; set; } = 0; - } -} diff --git a/SourceGit/Git/Branch.cs b/SourceGit/Git/Branch.cs deleted file mode 100644 index 5410c9b6..00000000 --- a/SourceGit/Git/Branch.cs +++ /dev/null @@ -1,190 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace SourceGit.Git { - - /// - /// Git branch - /// - public class Branch { - private static readonly string PRETTY_FORMAT = @"$%(refname)$%(objectname)$%(HEAD)$%(upstream)$%(upstream:track)$%(contents:subject)"; - private static readonly Regex PARSE = new Regex(@"\$(.*)\$(.*)\$([\* ])\$(.*)\$(.*?)\$(.*)"); - private static readonly Regex AHEAD = new Regex(@"ahead (\d+)"); - private static readonly Regex BEHIND = new Regex(@"behind (\d+)"); - - /// - /// Branch type. - /// - public enum Type { - Normal, - Feature, - Release, - Hotfix, - } - - /// - /// Branch name - /// - public string Name { get; set; } = ""; - - /// - /// Full name. - /// - public string FullName { get; set; } = ""; - - /// - /// Head ref - /// - public string Head { get; set; } = ""; - - /// - /// Subject for head ref. - /// - public string HeadSubject { get; set; } = ""; - - /// - /// Is local branch - /// - public bool IsLocal { get; set; } = false; - - /// - /// Branch type. - /// - public Type Kind { get; set; } = Type.Normal; - - /// - /// Remote name. Only used for remote branch - /// - public string Remote { get; set; } = ""; - - /// - /// Upstream. Only used for local branches. - /// - public string Upstream { get; set; } - - /// - /// Track information for upstream. Only used for local branches. - /// - public string UpstreamTrack { get; set; } - - /// - /// Is current branch. Only used for local branches. - /// - public bool IsCurrent { get; set; } - - /// - /// Is this branch's HEAD same with upstream? - /// - public bool IsSameWithUpstream => string.IsNullOrEmpty(UpstreamTrack); - - /// - /// Enable filter in log histories. - /// - public bool IsFiltered { get; set; } - - /// - /// Load branches. - /// - /// - public static List Load(Repository repo) { - var localPrefix = "refs/heads/"; - var remotePrefix = "refs/remotes/"; - var branches = new List(); - var remoteBranches = new List(); - - repo.RunCommand("branch -l --all -v --format=\"" + PRETTY_FORMAT + "\"", line => { - var match = PARSE.Match(line); - if (!match.Success) return; - - var branch = new Branch(); - var refname = match.Groups[1].Value; - if (refname.EndsWith("/HEAD")) return; - - if (refname.StartsWith(localPrefix, StringComparison.Ordinal)) { - branch.Name = refname.Substring(localPrefix.Length); - branch.IsLocal = true; - } else if (refname.StartsWith(remotePrefix, StringComparison.Ordinal)) { - var name = refname.Substring(remotePrefix.Length); - branch.Remote = name.Substring(0, name.IndexOf('/')); - branch.Name = name; - branch.IsLocal = false; - remoteBranches.Add(refname); - } - - branch.FullName = refname; - branch.Head = match.Groups[2].Value; - branch.IsCurrent = match.Groups[3].Value == "*"; - branch.Upstream = match.Groups[4].Value; - branch.UpstreamTrack = ParseTrack(match.Groups[5].Value); - branch.HeadSubject = match.Groups[6].Value; - - branches.Add(branch); - }); - - // Fixed deleted remote branch - foreach (var b in branches) { - if (!string.IsNullOrEmpty(b.Upstream) && !remoteBranches.Contains(b.Upstream)) { - b.Upstream = null; - } - } - - return branches; - } - - /// - /// Create new branch. - /// - /// - /// - /// - public static void Create(Repository repo, string name, string startPoint) { - var errs = repo.RunCommand($"branch {name} {startPoint}", null); - if (errs != null) App.RaiseError(errs); - } - - /// - /// Rename branch - /// - /// - /// - public void Rename(Repository repo, string name) { - var errs = repo.RunCommand($"branch -M {Name} {name}", null); - if (errs != null) App.RaiseError(errs); - } - - /// - /// Delete branch. - /// - /// - public void Delete(Repository repo) { - string errs = null; - - if (!IsLocal) { - errs = repo.RunCommand($"-c credential.helper=manager push {Remote} --delete {Name.Substring(Name.IndexOf('/')+1)}", null); - } else { - errs = repo.RunCommand($"branch -D {Name}", null); - } - - if (errs != null) App.RaiseError(errs); - } - - private static string ParseTrack(string data) { - if (string.IsNullOrEmpty(data)) return ""; - - string track = ""; - - var ahead = AHEAD.Match(data); - if (ahead.Success) { - track += ahead.Groups[1].Value + "↑ "; - } - - var behind = BEHIND.Match(data); - if (behind.Success) { - track += behind.Groups[1].Value + "↓"; - } - - return track.Trim(); - } - } -} diff --git a/SourceGit/Git/Change.cs b/SourceGit/Git/Change.cs deleted file mode 100644 index c5f9ca4a..00000000 --- a/SourceGit/Git/Change.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System.Text.RegularExpressions; - -namespace SourceGit.Git { - - /// - /// Changed file status. - /// - public class Change { - private static readonly Regex FORMAT = new Regex(@"^(\s?[\w\?]{1,4})\s+(.+)$"); - - /// - /// Status Code - /// - public enum Status { - None, - Modified, - Added, - Deleted, - Renamed, - Copied, - Unmerged, - Untracked, - } - - /// - /// Index status - /// - public Status Index { get; set; } - - /// - /// Work tree status. - /// - public Status WorkTree { get; set; } - - /// - /// Current file path. - /// - public string Path { get; set; } - - /// - /// Original file path before this revision. - /// - public string OriginalPath { get; set; } - - /// - /// Staged(added) in index? - /// - public bool IsAddedToIndex { - get { - if (Index == Status.None || Index == Status.Untracked) return false; - return true; - } - } - - /// - /// Is conflict? - /// - public bool IsConflit { - get { - if (Index == Status.Unmerged || WorkTree == Status.Unmerged) return true; - if (Index == Status.Added && WorkTree == Status.Added) return true; - if (Index == Status.Deleted && WorkTree == Status.Deleted) return true; - return false; - } - } - - /// - /// Parse change for `--name-status` data. - /// - /// Raw data. - /// Read from commit? - /// Parsed change instance. - public static Change Parse(string data, bool fromCommit = false) { - var match = FORMAT.Match(data); - if (!match.Success) return null; - - var change = new Change() { Path = match.Groups[2].Value }; - var status = match.Groups[1].Value; - - if (fromCommit) { - switch (status[0]) { - case 'M': change.Set(Status.Modified); break; - case 'A': change.Set(Status.Added); break; - case 'D': change.Set(Status.Deleted); break; - case 'R': change.Set(Status.Renamed); break; - case 'C': change.Set(Status.Copied); break; - default: return null; - } - } else { - switch (status) { - case " M": change.Set(Status.None, Status.Modified); break; - case " A": change.Set(Status.None, Status.Added); break; - case " D": change.Set(Status.None, Status.Deleted); break; - case " R": change.Set(Status.None, Status.Renamed); break; - case " C": change.Set(Status.None, Status.Copied); break; - case "M": change.Set(Status.Modified, Status.None); break; - case "MM": change.Set(Status.Modified, Status.Modified); break; - case "MD": change.Set(Status.Modified, Status.Deleted); break; - case "A": change.Set(Status.Added, Status.None); break; - case "AM": change.Set(Status.Added, Status.Modified); break; - case "AD": change.Set(Status.Added, Status.Deleted); break; - case "D": change.Set(Status.Deleted, Status.None); break; - case "R": change.Set(Status.Renamed, Status.None); break; - case "RM": change.Set(Status.Renamed, Status.Modified); break; - case "RD": change.Set(Status.Renamed, Status.Deleted); break; - case "C": change.Set(Status.Copied, Status.None); break; - case "CM": change.Set(Status.Copied, Status.Modified); break; - case "CD": change.Set(Status.Copied, Status.Deleted); break; - case "DR": change.Set(Status.Deleted, Status.Renamed); break; - case "DC": change.Set(Status.Deleted, Status.Copied); break; - case "DD": change.Set(Status.Deleted, Status.Deleted); break; - case "AU": change.Set(Status.Added, Status.Unmerged); break; - case "UD": change.Set(Status.Unmerged, Status.Deleted); break; - case "UA": change.Set(Status.Unmerged, Status.Added); break; - case "DU": change.Set(Status.Deleted, Status.Unmerged); break; - case "AA": change.Set(Status.Added, Status.Added); break; - case "UU": change.Set(Status.Unmerged, Status.Unmerged); break; - case "??": change.Set(Status.Untracked, Status.Untracked); break; - default: return null; - } - } - - if (change.Path[0] == '"') change.Path = change.Path.Substring(1, change.Path.Length - 2); - if (!string.IsNullOrEmpty(change.OriginalPath) && change.OriginalPath[0] == '"') change.OriginalPath = change.OriginalPath.Substring(1, change.OriginalPath.Length - 2); - return change; - } - - private void Set(Status index, Status workTree = Status.None) { - Index = index; - WorkTree = workTree; - - if (index == Status.Renamed || workTree == Status.Renamed) { - var idx = Path.IndexOf('\t'); - if (idx >= 0) { - OriginalPath = Path.Substring(0, idx); - Path = Path.Substring(idx + 1); - } else { - idx = Path.IndexOf(" -> "); - if (idx > 0) { - OriginalPath = Path.Substring(0, idx); - Path = Path.Substring(idx + 4); - } - } - } - } - } -} diff --git a/SourceGit/Git/Commit.cs b/SourceGit/Git/Commit.cs deleted file mode 100644 index ef0480f7..00000000 --- a/SourceGit/Git/Commit.cs +++ /dev/null @@ -1,296 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Text.RegularExpressions; - -namespace SourceGit.Git { - - /// - /// Git commit information. - /// - public class Commit { - private static readonly string GPGSIG_START = "gpgsig -----BEGIN PGP SIGNATURE-----"; - private static readonly string GPGSIG_END = " -----END PGP SIGNATURE-----"; - - /// - /// Object in commit. - /// - public class Object { - public enum Type { - Tag, - Blob, - Tree, - Commit, - } - - public string Path { get; set; } - public Type Kind { get; set; } - public string SHA { get; set; } - } - - /// - /// SHA - /// - public string SHA { get; set; } - - /// - /// Short SHA. - /// - public string ShortSHA => SHA.Substring(0, 8); - - /// - /// Parent commit SHAs. - /// - public List Parents { get; set; } = new List(); - - /// - /// Author - /// - public User Author { get; set; } = new User(); - - /// - /// Committer. - /// - public User Committer { get; set; } = new User(); - - /// - /// Subject - /// - public string Subject { get; set; } = ""; - - /// - /// Extra message. - /// - public string Message { get; set; } = ""; - - /// - /// HEAD commit? - /// - public bool IsHEAD { get; set; } = false; - - /// - /// Merged in current branch? - /// - public bool IsMerged { get; set; } = false; - - /// - /// X offset in graph - /// - public double GraphOffset { get; set; } = 0; - - /// - /// Has decorators. - /// - public bool HasDecorators => Decorators.Count > 0; - - /// - /// Decorators. - /// - public List Decorators { get; set; } = new List(); - - /// - /// Read commits. - /// - /// Repository - /// Limitations - /// Parsed commits. - public static List Load(Repository repo, string limit) { - List commits = new List(); - Commit current = null; - bool bSkippingGpgsig = false; - bool findHead = false; - - repo.RunCommand("log --date-order --decorate=full --pretty=raw " + limit, line => { - if (bSkippingGpgsig) { - if (line.StartsWith(GPGSIG_END, StringComparison.Ordinal)) bSkippingGpgsig = false; - return; - } else if (line.StartsWith(GPGSIG_START, StringComparison.Ordinal)) { - bSkippingGpgsig = true; - return; - } - - if (line.StartsWith("commit ", StringComparison.Ordinal)) { - if (current != null) { - current.Message = current.Message.TrimEnd(); - commits.Add(current); - } - - current = new Commit(); - ParseSHA(current, line.Substring("commit ".Length)); - if (!findHead) findHead = current.IsHEAD; - return; - } - - if (current == null) return; - - if (line.StartsWith("tree ", StringComparison.Ordinal)) { - return; - } else if (line.StartsWith("parent ", StringComparison.Ordinal)) { - current.Parents.Add(line.Substring("parent ".Length)); - } else if (line.StartsWith("author ", StringComparison.Ordinal)) { - current.Author.Parse(line); - } else if (line.StartsWith("committer ", StringComparison.Ordinal)) { - current.Committer.Parse(line); - } else if (string.IsNullOrEmpty(current.Subject)) { - current.Subject = line.Trim(); - } else { - current.Message += (line.Trim() + "\n"); - } - }); - - if (current != null) { - current.Message = current.Message.TrimEnd(); - commits.Add(current); - } - - if (!findHead && commits.Count > 0) { - var startInfo = new ProcessStartInfo(); - startInfo.FileName = Preference.Instance.GitExecutable; - startInfo.Arguments = $"merge-base --is-ancestor {commits[0].SHA} HEAD"; - startInfo.WorkingDirectory = repo.Path; - startInfo.UseShellExecute = false; - startInfo.CreateNoWindow = true; - startInfo.RedirectStandardOutput = false; - startInfo.RedirectStandardError = false; - - var proc = new Process() { StartInfo = startInfo }; - proc.Start(); - proc.WaitForExit(); - - commits[0].IsMerged = proc.ExitCode == 0; - proc.Close(); - } - - return commits; - } - - /// - /// Get changed file list. - /// - /// - /// - public List GetChanges(Repository repo) { - var changes = new List(); - var regex = new Regex(@"^[MADRC]\d*\s*.*$"); - - var errs = repo.RunCommand($"show --name-status {SHA}", line => { - if (!regex.IsMatch(line)) return; - - var change = Change.Parse(line, true); - if (change != null) changes.Add(change); - }); - - if (errs != null) App.RaiseError(errs); - return changes; - } - - /// - /// Get revision files. - /// - /// - /// - public List GetFiles(Repository repo) { - var files = new List(); - var test = new Regex(@"^\d+\s+(\w+)\s+([0-9a-f]+)\s+(.*)$"); - - var errs = repo.RunCommand($"ls-tree -r {SHA}", line => { - var match = test.Match(line); - if (!match.Success) return; - - var obj = new Object(); - obj.Path = match.Groups[3].Value; - obj.Kind = Object.Type.Blob; - obj.SHA = match.Groups[2].Value; - - switch (match.Groups[1].Value) { - case "tag": obj.Kind = Object.Type.Tag; break; - case "blob": obj.Kind = Object.Type.Blob; break; - case "tree": obj.Kind = Object.Type.Tree; break; - case "commit": obj.Kind = Object.Type.Commit; break; - } - - files.Add(obj); - }); - - if (errs != null) App.RaiseError(errs); - return files; - } - - /// - /// Get file content. - /// - /// - /// - /// - public string GetTextFileContent(Repository repo, string file, out bool isBinary) { - var data = new List(); - var count = 0; - var binary = false; - - var errs = repo.RunCommand($"show {SHA}:\"{file}\"", line => { - if (binary) return; - - count++; - if (data.Count >= 1000) return; - - if (line.IndexOf('\0') >= 0) { - binary = true; - data.Clear(); - data.Add("BINARY FILE PREVIEW NOT SUPPORTED!"); - return; - } - - data.Add(line); - }); - - if (!binary && count > 1000) { - data.Add("..."); - data.Add($"Total {count} lines. Hide {count-1000} lines."); - } - - isBinary = binary; - - if (errs != null) App.RaiseError(errs); - return string.Join("\n", data); - } - - private static void ParseSHA(Commit commit, string data) { - var decoratorStart = data.IndexOf('('); - if (decoratorStart < 0) { - commit.SHA = data.Trim(); - return; - } - - commit.SHA = data.Substring(0, decoratorStart).Trim(); - - var subs = data.Substring(decoratorStart + 1).Split(new char[] { ',', ')', '(' }, StringSplitOptions.RemoveEmptyEntries); - foreach (var sub in subs) { - var d = sub.Trim(); - if (d.StartsWith("tag: refs/tags/", StringComparison.Ordinal)) { - commit.Decorators.Add(new Decorator() { - Type = DecoratorType.Tag, - Name = d.Substring(15).Trim() - }); - } else if (d.EndsWith("/HEAD")) { - continue; - } else if (d.StartsWith("HEAD -> refs/heads/", StringComparison.Ordinal)) { - commit.IsHEAD = true; - commit.Decorators.Add(new Decorator() { - Type = DecoratorType.CurrentBranchHead, - Name = d.Substring(19).Trim() - }); - } else if (d.StartsWith("refs/heads/", StringComparison.Ordinal)) { - commit.Decorators.Add(new Decorator() { - Type = DecoratorType.LocalBranchHead, - Name = d.Substring(11).Trim() - }); - } else if (d.StartsWith("refs/remotes/", StringComparison.Ordinal)) { - commit.Decorators.Add(new Decorator() { - Type = DecoratorType.RemoteBranchHead, - Name = d.Substring(13).Trim() - }); - } - } - } - } -} diff --git a/SourceGit/Git/Decorator.cs b/SourceGit/Git/Decorator.cs deleted file mode 100644 index d9131712..00000000 --- a/SourceGit/Git/Decorator.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace SourceGit.Git { - - /// - /// Decorator type. - /// - public enum DecoratorType { - None, - CurrentBranchHead, - LocalBranchHead, - RemoteBranchHead, - Tag, - } - - /// - /// Commit decorator. - /// - public class Decorator { - public DecoratorType Type { get; set; } - public string Name { get; set; } - } -} diff --git a/SourceGit/Git/Diff.cs b/SourceGit/Git/Diff.cs deleted file mode 100644 index 7ce0190c..00000000 --- a/SourceGit/Git/Diff.cs +++ /dev/null @@ -1,263 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; - -namespace SourceGit.Git { - - /// - /// Diff helper. - /// - public class Diff { - private static readonly Regex REG_INDICATOR = new Regex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@", RegexOptions.None); - - /// - /// Line mode. - /// - public enum LineMode { - Normal, - Indicator, - Empty, - Added, - Deleted, - } - - /// - /// Side - /// - public enum Side { - Left, - Right, - Both, - } - - /// - /// Binary change. - /// - public class BinaryChange { - public long Size = 0; - public long PreSize = 0; - } - - /// - /// Block - /// - public class Block { - public Side Side = Side.Both; - public LineMode Mode = LineMode.Normal; - public int LeftStart = 0; - public int RightStart = 0; - public int Count = 0; - public StringBuilder Builder = new StringBuilder(); - - public bool IsLeftDelete => Side == Side.Left && Mode == LineMode.Deleted; - public bool IsRightAdded => Side == Side.Right && Mode == LineMode.Added; - public bool IsBothSideNormal => Side == Side.Both && Mode == LineMode.Normal; - public bool CanShowNumber => Mode != LineMode.Indicator && Mode != LineMode.Empty; - - public void Append(string data) { - if (Count > 0) Builder.AppendLine(); - Builder.Append(data); - Count++; - } - } - - /// - /// Diff result. - /// - public class Result { - public bool IsValid = false; - public bool IsBinary = false; - public List Blocks = new List(); - public int LeftLineCount = 0; - public int RightLineCount = 0; - - public void SetBinary() { - IsValid = true; - IsBinary = true; - } - - public void Add(Block b) { - if (b.Count == 0) return; - - switch (b.Side) { - case Side.Left: - LeftLineCount += b.Count; - break; - case Side.Right: - RightLineCount += b.Count; - break; - default: - LeftLineCount += b.Count; - RightLineCount += b.Count; - break; - } - - Blocks.Add(b); - } - - public void Fit() { - if (LeftLineCount > RightLineCount) { - var b = new Block(); - b.Side = Side.Right; - b.Mode = LineMode.Empty; - - var delta = LeftLineCount - RightLineCount; - for (int i = 0; i < delta; i++) b.Append(""); - - Add(b); - } else if (LeftLineCount < RightLineCount) { - var b = new Block(); - b.Side = Side.Left; - b.Mode = LineMode.Empty; - - var delta = RightLineCount - LeftLineCount; - for (int i = 0; i < delta; i++) b.Append(""); - - Add(b); - } - } - } - - /// - /// Run diff process. - /// - /// - /// - /// - public static Result Run(Repository repo, string args) { - var rs = new Result(); - var current = new Block(); - var left = 0; - var right = 0; - - repo.RunCommand($"diff --ignore-cr-at-eol {args}", line => { - if (rs.IsBinary) return; - - if (!rs.IsValid) { - var match = REG_INDICATOR.Match(line); - if (!match.Success) { - if (line.StartsWith("Binary ")) rs.SetBinary(); - return; - } - - rs.IsValid = true; - left = int.Parse(match.Groups[1].Value); - right = int.Parse(match.Groups[2].Value); - current.Mode = LineMode.Indicator; - current.Append(line); - } else { - if (line[0] == '-') { - if (current.IsLeftDelete) { - current.Append(line.Substring(1)); - } else { - rs.Add(current); - - current = new Block(); - current.Side = Side.Left; - current.Mode = LineMode.Deleted; - current.LeftStart = left; - current.Append(line.Substring(1)); - } - - left++; - } else if (line[0] == '+') { - if (current.IsRightAdded) { - current.Append(line.Substring(1)); - } else { - rs.Add(current); - - current = new Block(); - current.Side = Side.Right; - current.Mode = LineMode.Added; - current.RightStart = right; - current.Append(line.Substring(1)); - } - - right++; - } else if (line[0] == '\\') { - var tmp = new Block(); - tmp.Side = current.Side; - tmp.Mode = LineMode.Indicator; - tmp.Append(line.Substring(1)); - - rs.Add(current); - rs.Add(tmp); - rs.Fit(); - - current = new Block(); - current.LeftStart = left; - current.RightStart = right; - } else { - var match = REG_INDICATOR.Match(line); - if (match.Success) { - rs.Add(current); - rs.Fit(); - - left = int.Parse(match.Groups[1].Value); - right = int.Parse(match.Groups[2].Value); - - current = new Block(); - current.Mode = LineMode.Indicator; - current.Append(line); - } else { - if (current.IsBothSideNormal) { - current.Append(line.Substring(1)); - } else { - rs.Add(current); - rs.Fit(); - - current = new Block(); - current.LeftStart = left; - current.RightStart = right; - current.Append(line.Substring(1)); - } - - left++; - right++; - } - } - } - }); - - rs.Add(current); - rs.Fit(); - - if (rs.IsBinary) rs.Blocks.Clear(); - return rs; - } - - /// - /// Get file size changes for binary file. - /// - /// - /// - /// - /// - /// - public static BinaryChange GetSizeChange(Repository repo, string[] revisions, string path, string orgPath = null) { - var change = new BinaryChange(); - - if (revisions.Length == 0) { // Compare working copy with HEAD - change.Size = new FileInfo(Path.Combine(repo.Path, path)).Length; - change.PreSize = repo.GetFileSize("HEAD", path); - } else if (revisions.Length == 1) { // Compare HEAD with given revision. - change.Size = repo.GetFileSize("HEAD", path); - if (!string.IsNullOrEmpty(orgPath)) { - change.PreSize = repo.GetFileSize(revisions[0], orgPath); - } else { - change.PreSize = repo.GetFileSize(revisions[0], path); - } - } else { - change.Size = repo.GetFileSize(revisions[1], path); - if (!string.IsNullOrEmpty(orgPath)) { - change.PreSize = repo.GetFileSize(revisions[0], orgPath); - } else { - change.PreSize = repo.GetFileSize(revisions[0], path); - } - } - - return change; - } - } -} diff --git a/SourceGit/Git/MergeTool.cs b/SourceGit/Git/MergeTool.cs deleted file mode 100644 index 8fda9492..00000000 --- a/SourceGit/Git/MergeTool.cs +++ /dev/null @@ -1,202 +0,0 @@ -using Microsoft.Win32; -using SourceGit.UI; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SourceGit.Git { - - /// - /// External merge tool - /// - public class MergeTool { - - /// - /// Display name - /// - public string Name { get; set; } - - /// - /// Executable file name. - /// - public string ExecutableName { get; set; } - - /// - /// Command line parameter. - /// - public string Parameter { get; set; } - - /// - /// Auto finder. - /// - public Func Finder { get; set; } - - /// - /// Is this merge tool configured. - /// - public bool IsConfigured => !string.IsNullOrEmpty(ExecutableName); - - /// - /// Supported merge tools. - /// - public static List Supported = new List() { - new MergeTool("--", "", "", FindInvalid), - new MergeTool("Araxis Merge", "Compare.exe", "/wait /merge /3 /a1 \"$BASE\" \"$REMOTE\" \"$LOCAL\" \"$MERGED\"", FindAraxisMerge), - new MergeTool("Beyond Compare 4", "BComp.exe", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\"", FindBCompare), - new MergeTool("KDiff3", "kdiff3.exe", "\"$REMOTE\" -b \"$BASE\" \"$LOCAL\" -o \"$MERGED\"", FindKDiff3), - new MergeTool("P4Merge", "p4merge.exe", "\"$BASE\" \"$REMOTE\" \"$LOCAL\" \"$MERGED\"", FindP4Merge), - new MergeTool("Tortoise Merge", "TortoiseMerge.exe", "-base:\"$BASE\" -theirs:\"$REMOTE\" -mine:\"$LOCAL\" -merged:\"$MERGED\"", FindTortoiseMerge), - new MergeTool("Visual Studio 2017/2019", "vsDiffMerge.exe", "\"$REMOTE\" \"$LOCAL\" \"$BASE\" \"$MERGED\" //m", FindVSMerge), - new MergeTool("Visual Studio Code", "Code.exe", "-n --wait \"$MERGED\"", FindVSCode), - }; - - /// - /// Finder for invalid merge tool. - /// - /// - public static string FindInvalid() { - return "--"; - } - - /// - /// Find araxis merge tool install path. - /// - /// - public static string FindAraxisMerge() { - var path = @"C:\Program Files\Araxis\Araxis Merge\Compare.exe"; - if (File.Exists(path)) return path; - return ""; - } - - /// - /// Find kdiff3.exe by registry. - /// - /// - public static string FindKDiff3() { - var root = RegistryKey.OpenBaseKey( - RegistryHive.LocalMachine, - Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); - - var kdiff = root.OpenSubKey(@"SOFTWARE\KDiff3\diff-ext"); - if (kdiff == null) return ""; - return kdiff.GetValue("diffcommand") as string; - } - - /// - /// Finder for p4merge - /// - /// - public static string FindP4Merge() { - var path = @"C:\Program Files\Perforce\p4merge.exe"; - if (File.Exists(path)) return path; - return ""; - } - - /// - /// Find BComp.exe by registry. - /// - /// - public static string FindBCompare() { - var root = RegistryKey.OpenBaseKey( - RegistryHive.LocalMachine, - Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); - - var bc = root.OpenSubKey(@"SOFTWARE\Scooter Software\Beyond Compare"); - if (bc == null) return ""; - - var exec = bc.GetValue("ExePath") as string; - var dir = Path.GetDirectoryName(exec); - return $"{dir}\\BComp.exe"; - } - - /// - /// Find TortoiseMerge.exe by registry. - /// - /// - public static string FindTortoiseMerge() { - var root = RegistryKey.OpenBaseKey( - RegistryHive.LocalMachine, - Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); - - var tortoiseSVN = root.OpenSubKey("SOFTWARE\\TortoiseSVN"); - if (tortoiseSVN == null) return ""; - return tortoiseSVN.GetValue("TMergePath") as string; - } - - /// - /// Find vsDiffMerge.exe. - /// - /// - public static string FindVSMerge() { - var dir = @"C:\Program Files (x86)\Microsoft Visual Studio"; - if (Directory.Exists($"{dir}\\2019")) { - dir += "\\2019"; - } else if (Directory.Exists($"{dir}\\2017")) { - dir += "\\2017"; - } else { - return ""; - } - - if (Directory.Exists($"{dir}\\Community")) { - dir += "\\Community"; - } else if (Directory.Exists($"{dir}\\Enterprise")) { - dir += "\\Enterprise"; - } else if (Directory.Exists($"{dir}\\Professional")) { - dir += "\\Professional"; - } else { - return ""; - } - - return $"{dir}\\Common7\\IDE\\CommonExtensions\\Microsoft\\TeamFoundation\\Team Explorer\\vsDiffMerge.exe"; - } - - /// - /// Find VSCode executable file path. - /// - /// - public static string FindVSCode() { - var root = RegistryKey.OpenBaseKey( - RegistryHive.LocalMachine, - Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32); - - var vscode = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{C26E74D1-022E-4238-8B9D-1E7564A36CC9}_is1"); - if (vscode != null) { - return vscode.GetValue("DisplayIcon") as string; - } - - vscode = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{1287CAD5-7C8D-410D-88B9-0D1EE4A83FF2}_is1"); - if (vscode != null) { - return vscode.GetValue("DisplayIcon") as string; - } - - vscode = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{F8A2A208-72B3-4D61-95FC-8A65D340689B}_is1"); - if (vscode != null) { - return vscode.GetValue("DisplayIcon") as string; - } - - vscode = root.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{EA457B21-F73E-494C-ACAB-524FDE069978}_is1"); - if (vscode != null) { - return vscode.GetValue("DisplayIcon") as string; - } - - return ""; - } - - /// - /// Constructor. - /// - /// - /// - /// - /// - public MergeTool(string name, string exe, string param, Func finder) { - Name = name; - ExecutableName = exe; - Parameter = param; - Finder = finder; - } - } -} diff --git a/SourceGit/Git/Preference.cs b/SourceGit/Git/Preference.cs deleted file mode 100644 index 092462fe..00000000 --- a/SourceGit/Git/Preference.cs +++ /dev/null @@ -1,300 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Xml.Serialization; - -namespace SourceGit.Git { - - /// - /// User's preference settings. Serialized to - /// - public class Preference { - - /// - /// Group(Virtual folder) for watched repositories. - /// - public class Group { - /// - /// Unique ID of this group. - /// - public string Id { get; set; } - /// - /// Display name. - /// - public string Name { get; set; } - /// - /// Parent ID. - /// - public string ParentId { get; set; } - /// - /// Cache UI IsExpended status. - /// - public bool IsExpended { get; set; } - } - - #region STATICS - /// - /// Storage path for Preference. - /// - private static readonly string SAVE_PATH = Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), - "SourceGit", - "preference.xml"); - /// - /// Runtime singleton instance. - /// - private static Preference instance = null; - public static Preference Instance { - get { - if (instance == null) Load(); - return instance; - } - set { - instance = value; - } - } - #endregion - - #region SETTING_GIT - /// - /// Git executable file path. - /// - public string GitExecutable { get; set; } - /// - /// Default clone directory. - /// - public string GitDefaultCloneDir { get; set; } - #endregion - - #region SETTING_MERGE_TOOL - /// - /// Selected merge tool. - /// - public int MergeTool { get; set; } = 0; - /// - /// Executable file path for merge tool. - /// - public string MergeExecutable { get; set; } = "--"; - #endregion - - #region SETTING_UI - /// - /// Main window's width - /// - public double UIMainWindowWidth { get; set; } - /// - /// Main window's height - /// - public double UIMainWindowHeight { get; set; } - /// - /// Use light color theme. - /// - public bool UIUseLightTheme { get; set; } - /// - /// Show/Hide tags' list view. - /// - public bool UIShowTags { get; set; } = true; - /// - /// Use horizontal layout for histories. - /// - public bool UIUseHorizontalLayout { get; set; } - /// - /// Use list instead of tree in unstaged view - /// - public bool UIUseListInUnstaged { get; set; } - /// - /// Use list instead of tree in staged view. - /// - public bool UIUseListInStaged { get; set; } - /// - /// Use list instead of tree in change view. - /// - public bool UIUseListInChanges { get; set; } - #endregion - - #region SETTING_REPOS - /// - /// Groups for repositories. - /// - public List Groups { get; set; } = new List(); - /// - /// Watched repositories. - /// - public List Repositories { get; set; } = new List(); - #endregion - - #region METHODS_LOAD_SAVE - /// - /// Load preference from disk. - /// - /// Loaded preference instance. - public static void Load() { - if (!File.Exists(SAVE_PATH)) { - instance = new Preference(); - return; - } - - try { - var stream = new FileStream(SAVE_PATH, FileMode.Open); - var reader = new XmlSerializer(typeof(Preference)); - instance = (Preference)reader.Deserialize(stream); - stream.Close(); - } catch { - instance = new Preference(); - } - } - - /// - /// Save current preference into disk. - /// - public static void Save() { - if (instance == null) return; - - var dir = Path.GetDirectoryName(SAVE_PATH); - if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); - - var stream = new FileStream(SAVE_PATH, FileMode.Create); - var writer = new XmlSerializer(typeof(Preference)); - writer.Serialize(stream, instance); - stream.Flush(); - stream.Close(); - } - #endregion - - #region METHODS_ON_GROUP - /// - /// Add new group(virtual folder). - /// - /// Display name. - /// Parent group ID. - /// Added group instance. - public Group AddGroup(string name, string parentId) { - var group = new Group() { - Name = name, - Id = Guid.NewGuid().ToString(), - ParentId = parentId, - IsExpended = false, - }; - - Groups.Add(group); - Groups.Sort((l, r) => l.Name.CompareTo(r.Name)); - - return group; - } - - /// - /// Find group by ID. - /// - /// Unique ID - /// Founded group's instance. - public Group FindGroup(string id) { - foreach (var group in Groups) { - if (group.Id == id) return group; - } - return null; - } - - /// - /// Rename group. - /// - /// Unique ID - /// New name. - public void RenameGroup(string id, string newName) { - foreach (var group in Groups) { - if (group.Id == id) { - group.Name = newName; - break; - } - } - - Groups.Sort((l, r) => l.Name.CompareTo(r.Name)); - } - - /// - /// Remove a group. - /// - /// Unique ID - public void RemoveGroup(string id) { - int removedIdx = -1; - - for (int i = 0; i < Groups.Count; i++) { - if (Groups[i].Id == id) { - removedIdx = i; - break; - } - } - - if (removedIdx >= 0) Groups.RemoveAt(removedIdx); - } - #endregion - - #region METHODS_ON_REPOS - /// - /// Add repository. - /// - /// Local storage path. - /// Group's ID - /// Added repository instance. - public Repository AddRepository(string path, string groupId) { - var repo = FindRepository(path); - if (repo != null) return repo; - - var dir = new DirectoryInfo(path); - repo = new Repository() { - Path = dir.FullName, - Name = dir.Name, - GroupId = groupId, - LastOpenTime = 0, - }; - - Repositories.Add(repo); - Repositories.Sort((l, r) => l.Name.CompareTo(r.Name)); - return repo; - } - - /// - /// Find repository by path. - /// - /// Local storage path. - /// Founded repository instance. - public Repository FindRepository(string path) { - var dir = new DirectoryInfo(path); - foreach (var repo in Repositories) { - if (repo.Path == dir.FullName) return repo; - } - return null; - } - - /// - /// Change a repository's display name in RepositoryManager. - /// - /// Local storage path. - /// New name - public void RenameRepository(string path, string newName) { - var repo = FindRepository(path); - if (repo == null) return; - - repo.Name = newName; - Repositories.Sort((l, r) => l.Name.CompareTo(r.Name)); - } - - /// - /// Remove a repository in RepositoryManager. - /// - /// Local storage path. - public void RemoveRepository(string path) { - var dir = new DirectoryInfo(path); - var removedIdx = -1; - - for (int i = 0; i < Repositories.Count; i++) { - if (Repositories[i].Path == dir.FullName) { - removedIdx = i; - break; - } - } - - if (removedIdx >= 0) Repositories.RemoveAt(removedIdx); - } - #endregion - } -} diff --git a/SourceGit/Git/Remote.cs b/SourceGit/Git/Remote.cs deleted file mode 100644 index ed32e810..00000000 --- a/SourceGit/Git/Remote.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace SourceGit.Git { - - /// - /// Git remote - /// - public class Remote { - private static readonly Regex FORMAT = new Regex(@"^([\w\.\-]+)\s*(\S+).*$"); - - /// - /// Name of this remote - /// - public string Name { get; set; } - - /// - /// URL - /// - public string URL { get; set; } - - /// - /// Parsing remote - /// - /// Repository - /// - public static List Load(Repository repo) { - var remotes = new List(); - var added = new List(); - - repo.RunCommand("remote -v", data => { - var match = FORMAT.Match(data); - if (!match.Success) return; - - var remote = new Remote() { - Name = match.Groups[1].Value, - URL = match.Groups[2].Value, - }; - - if (added.Contains(remote.Name)) return; - - added.Add(remote.Name); - remotes.Add(remote); - }); - - return remotes; - } - - /// - /// Add new remote - /// - /// - /// - /// - public static void Add(Repository repo, string name, string url) { - var errs = repo.RunCommand($"remote add {name} {url}", null); - if (errs != null) { - App.RaiseError(errs); - } else { - repo.Fetch(new Remote() { Name = name }, true, null); - } - } - - /// - /// Delete remote. - /// - /// - /// - public static void Delete(Repository repo, string remote) { - var errs = repo.RunCommand($"remote remove {remote}", null); - if (errs != null) App.RaiseError(errs); - } - - /// - /// Edit remote. - /// - /// - /// - /// - public void Edit(Repository repo, string name, string url) { - string errs = null; - - if (name != Name) { - errs = repo.RunCommand($"remote rename {Name} {name}", null); - if (errs != null) { - App.RaiseError(errs); - return; - } - } - - if (url != URL) { - errs = repo.RunCommand($"remote set-url {name} {url}", null); - if (errs != null) App.RaiseError(errs); - } - } - } -} diff --git a/SourceGit/Git/Repository.cs b/SourceGit/Git/Repository.cs deleted file mode 100644 index 9efc74e3..00000000 --- a/SourceGit/Git/Repository.cs +++ /dev/null @@ -1,1163 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using System.Windows.Threading; -using System.Xml.Serialization; - -namespace SourceGit.Git { - - /// - /// Git repository - /// - public class Repository { - - #region HOOKS - public static Action OnOpen = null; - [XmlIgnore] public Action OnNavigateCommit = null; - [XmlIgnore] public Action OnWorkingCopyChanged = null; - [XmlIgnore] public Action OnTagChanged = null; - [XmlIgnore] public Action OnStashChanged = null; - [XmlIgnore] public Action OnBranchChanged = null; - [XmlIgnore] public Action OnCommitsChanged = null; - [XmlIgnore] public Action OnSubmoduleChanged = null; - #endregion - - #region PROPERTIES_SAVED - /// - /// Storage path. - /// - public string Path { get; set; } - /// - /// Display name. - /// - public string Name { get; set; } - /// - /// Owner group. - /// - public string GroupId { get; set; } - /// - /// Last open time(File time format). - /// - public long LastOpenTime { get; set; } - /// - /// Filters for logs. - /// - public List LogFilters { get; set; } = new List(); - /// - /// Last 10 Commit message. - /// - public List CommitMsgRecords { get; set; } = new List(); - /// - /// Commit template. - /// - public string CommitTemplate { get; set; } - #endregion - - #region PROPERTIES_RUNTIME - [XmlIgnore] public Repository Parent = null; - [XmlIgnore] public string GitDir = null; - - private List cachedRemotes = new List(); - private List cachedBranches = new List(); - private List cachedTags = new List(); - private FileSystemWatcher gitDirWatcher = null; - private FileSystemWatcher workingCopyWatcher = null; - private DispatcherTimer timer = null; - private bool isWatcherDisabled = false; - private long nextUpdateTags = 0; - private long nextUpdateLocalChanges = 0; - private long nextUpdateStashes = 0; - private long nextUpdateTree = 0; - - private string featurePrefix = null; - private string releasePrefix = null; - private string hotfixPrefix = null; - #endregion - - #region METHOD_PROCESS - /// - /// Read git config - /// - /// - /// - public string GetConfig(string key) { - var startInfo = new ProcessStartInfo(); - startInfo.FileName = Preference.Instance.GitExecutable; - startInfo.Arguments = $"config {key}"; - startInfo.WorkingDirectory = Path; - startInfo.UseShellExecute = false; - startInfo.CreateNoWindow = true; - startInfo.RedirectStandardOutput = true; - startInfo.StandardOutputEncoding = Encoding.UTF8; - - var proc = new Process() { StartInfo = startInfo }; - proc.Start(); - var output = proc.StandardOutput.ReadToEnd(); - proc.WaitForExit(); - proc.Close(); - - return output.Trim(); - } - - /// - /// Configure git. - /// - /// - /// - public void SetConfig(string key, string value) { - var startInfo = new ProcessStartInfo(); - startInfo.FileName = Preference.Instance.GitExecutable; - startInfo.Arguments = $"config {key} \"{value}\""; - startInfo.WorkingDirectory = Path; - startInfo.UseShellExecute = false; - startInfo.CreateNoWindow = true; - - var proc = new Process() { StartInfo = startInfo }; - proc.Start(); - proc.WaitForExit(); - proc.Close(); - } - - /// - /// Run git command without repository. - /// - /// Working directory. - /// Arguments for running git command. - /// Handler for output. - /// Handle error as output. - /// Errors if exists. - public static string RunCommand(string cwd, string args, Action outputHandler, bool includeError = false) { - var startInfo = new ProcessStartInfo(); - startInfo.FileName = Preference.Instance.GitExecutable; - startInfo.Arguments = "--no-pager -c core.quotepath=off " + args; - startInfo.WorkingDirectory = cwd; - startInfo.UseShellExecute = false; - startInfo.CreateNoWindow = true; - startInfo.RedirectStandardOutput = true; - startInfo.RedirectStandardError = true; - startInfo.StandardOutputEncoding = Encoding.UTF8; - startInfo.StandardErrorEncoding = Encoding.UTF8; - - var progressFilter = new Regex(@"\d+\%"); - var errs = new List(); - var proc = new Process() { StartInfo = startInfo }; - - proc.OutputDataReceived += (o, e) => { - if (e.Data == null) return; - outputHandler?.Invoke(e.Data); - }; - proc.ErrorDataReceived += (o, e) => { - if (e.Data == null) return; - if (includeError) outputHandler?.Invoke(e.Data); - if (string.IsNullOrEmpty(e.Data)) return; - if (progressFilter.IsMatch(e.Data)) return; - if (e.Data.StartsWith("remote: Counting objects:", StringComparison.Ordinal)) return; - errs.Add(e.Data); - }; - - proc.Start(); - proc.BeginOutputReadLine(); - proc.BeginErrorReadLine(); - proc.WaitForExit(); - - int exitCode = proc.ExitCode; - proc.Close(); - - if (exitCode != 0 && errs.Count > 0) { - return string.Join("\n", errs); - } else { - return null; - } - } - - /// - /// Create process for reading outputs/errors using git.exe - /// - /// Arguments for running git command. - /// Handler for output. - /// Handle error as output. - /// Errors if exists. - public string RunCommand(string args, Action outputHandler, bool includeError = false) { - return RunCommand(Path, args, outputHandler, includeError); - } - - /// - /// Create process and redirect output to file. - /// - /// Git command arguments. - /// File path to redirect output into. - public void RunAndRedirect(string args, string redirectTo) { - var startInfo = new ProcessStartInfo(); - startInfo.FileName = Preference.Instance.GitExecutable; - startInfo.Arguments = "--no-pager " + args; - startInfo.WorkingDirectory = Path; - startInfo.UseShellExecute = false; - startInfo.CreateNoWindow = true; - startInfo.RedirectStandardOutput = true; - startInfo.RedirectStandardError = true; - - var proc = new Process() { StartInfo = startInfo }; - proc.Start(); - - using (var writer = new FileStream(redirectTo, FileMode.OpenOrCreate)) { - proc.StandardOutput.BaseStream.CopyTo(writer); - } - - proc.WaitForExit(); - proc.Close(); - } - - /// - /// Assert command result and then update branches and commits. - /// - /// - public void AssertCommand(string err) { - if (!string.IsNullOrEmpty(err)) App.RaiseError(err); - - Branches(true); - OnBranchChanged?.Invoke(); - OnCommitsChanged?.Invoke(); - OnWorkingCopyChanged?.Invoke(); - OnTagChanged?.Invoke(); - - isWatcherDisabled = false; - } - #endregion - - #region METHOD_VALIDATIONS - /// - /// Is valid git directory. - /// - /// Local path. - /// - public static bool IsValid(string path) { - var startInfo = new ProcessStartInfo(); - startInfo.FileName = Preference.Instance.GitExecutable; - startInfo.Arguments = "rev-parse --git-dir"; - startInfo.WorkingDirectory = path; - startInfo.UseShellExecute = false; - startInfo.CreateNoWindow = true; - - try { - var proc = new Process() { StartInfo = startInfo }; - proc.Start(); - proc.WaitForExit(); - - var test = proc.ExitCode == 0; - proc.Close(); - return test; - } catch { - return false; - } - } - - /// - /// Is remote url valid. - /// - /// - /// - public static bool IsValidUrl(string url) { - return !string.IsNullOrEmpty(url) - && (url.StartsWith("http://", StringComparison.Ordinal) - || url.StartsWith("https://", StringComparison.Ordinal) - || url.StartsWith("git://", StringComparison.Ordinal) - || url.StartsWith("ssh://", StringComparison.Ordinal) - || url.StartsWith("file://", StringComparison.Ordinal)); - } - #endregion - - #region METHOD_OPEN_CLOSE - /// - /// Open repository. - /// - public void Open() { - LastOpenTime = DateTime.Now.ToFileTime(); - isWatcherDisabled = false; - - GitDir = ".git"; - RunCommand("rev-parse --git-dir", line => { - GitDir = line; - }); - if (!System.IO.Path.IsPathRooted(GitDir)) GitDir = System.IO.Path.Combine(Path, GitDir); - - var checkGitDir = new DirectoryInfo(GitDir); - if (!checkGitDir.Exists) { - App.RaiseError("GIT_DIR for this repository NOT FOUND!"); - return; - } else { - GitDir = checkGitDir.FullName; - } - - gitDirWatcher = new FileSystemWatcher(); - gitDirWatcher.Path = GitDir; - gitDirWatcher.Filter = "*"; - gitDirWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName; - gitDirWatcher.IncludeSubdirectories = true; - gitDirWatcher.Created += OnGitDirFSChanged; - gitDirWatcher.Renamed += OnGitDirFSChanged; - gitDirWatcher.Changed += OnGitDirFSChanged; - gitDirWatcher.Deleted += OnGitDirFSChanged; - gitDirWatcher.EnableRaisingEvents = true; - - workingCopyWatcher = new FileSystemWatcher(); - workingCopyWatcher.Path = Path; - workingCopyWatcher.Filter = "*"; - workingCopyWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.DirectoryName | NotifyFilters.FileName; - workingCopyWatcher.IncludeSubdirectories = true; - workingCopyWatcher.Created += OnWorkingCopyFSChanged; - workingCopyWatcher.Renamed += OnWorkingCopyFSChanged; - workingCopyWatcher.Changed += OnWorkingCopyFSChanged; - workingCopyWatcher.Deleted += OnWorkingCopyFSChanged; - workingCopyWatcher.EnableRaisingEvents = true; - - timer = new DispatcherTimer(); - timer.Tick += Tick; - timer.Interval = TimeSpan.FromSeconds(.1); - timer.Start(); - - featurePrefix = GetConfig("gitflow.prefix.feature"); - releasePrefix = GetConfig("gitflow.prefix.release"); - hotfixPrefix = GetConfig("gitflow.prefix.hotfix"); - - OnOpen?.Invoke(this); - } - - /// - /// Close repository. - /// - public void Close() { - OnBranchChanged = null; - OnCommitsChanged = null; - OnTagChanged = null; - OnStashChanged = null; - OnWorkingCopyChanged = null; - OnNavigateCommit = null; - OnSubmoduleChanged = null; - - cachedBranches.Clear(); - cachedRemotes.Clear(); - cachedTags.Clear(); - - gitDirWatcher.EnableRaisingEvents = false; - workingCopyWatcher.EnableRaisingEvents = false; - gitDirWatcher.Dispose(); - workingCopyWatcher.Dispose(); - timer.Stop(); - - gitDirWatcher = null; - workingCopyWatcher = null; - timer = null; - featurePrefix = null; - releasePrefix = null; - hotfixPrefix = null; - - GC.Collect(); - } - #endregion - - #region METHOD_WATCHER - public void SetWatcherEnabled(bool enabled) { - isWatcherDisabled = !enabled; - } - - private void Tick(object sender, EventArgs e) { - if (isWatcherDisabled) { - nextUpdateLocalChanges = 0; - nextUpdateStashes = 0; - nextUpdateTags = 0; - nextUpdateTree = 0; - return; - } - - var now = DateTime.Now.ToFileTime(); - if (nextUpdateLocalChanges > 0 && now >= nextUpdateLocalChanges) { - nextUpdateLocalChanges = 0; - OnWorkingCopyChanged?.Invoke(); - } - - if (nextUpdateTags > 0 && now >= nextUpdateTags) { - nextUpdateTags = 0; - OnTagChanged?.Invoke(); - } - - if (nextUpdateStashes > 0 && now >= nextUpdateStashes) { - nextUpdateStashes = 0; - OnStashChanged?.Invoke(); - } - - if (nextUpdateTree > 0 && now >= nextUpdateTree) { - nextUpdateTree = 0; - Branches(true); - OnBranchChanged?.Invoke(); - OnCommitsChanged?.Invoke(); - } - } - - private void OnGitDirFSChanged(object sender, FileSystemEventArgs e) { - if (string.IsNullOrEmpty(e.Name)) return; - if (e.Name.StartsWith("index")) return; - - if (e.Name.StartsWith("refs\\tags", StringComparison.Ordinal)) { - nextUpdateTags = DateTime.Now.AddSeconds(.5).ToFileTime(); - } else if (e.Name.StartsWith("refs\\stash", StringComparison.Ordinal)) { - nextUpdateStashes = DateTime.Now.AddSeconds(.5).ToFileTime(); - } else if (e.Name.EndsWith("_HEAD", StringComparison.Ordinal) || - e.Name.StartsWith("refs\\heads", StringComparison.Ordinal) || - e.Name.StartsWith("refs\\remotes", StringComparison.Ordinal)) { - nextUpdateTree = DateTime.Now.AddSeconds(.5).ToFileTime(); - } - } - - private void OnWorkingCopyFSChanged(object sender, FileSystemEventArgs e) { - if (string.IsNullOrEmpty(e.Name)) return; - if (e.Name == ".git" || e.Name.StartsWith(".git\\")) return; - - nextUpdateLocalChanges = DateTime.Now.AddSeconds(1.5).ToFileTime(); - } - #endregion - - #region METHOD_GITCOMMANDS - /// - /// Clone repository. - /// - /// Remote repository URL - /// Folder to clone into - /// Local name - /// - /// - public static Repository Clone(string url, string folder, string rName, string lName, Action onProgress) { - string RemoteName; - if (rName != null) { - RemoteName = $" --origin {rName}"; - } else { - RemoteName = null; - } - - var errs = RunCommand(folder, $"-c credential.helper=manager clone --progress --verbose {RemoteName} --recurse-submodules {url} {lName}", line => { - if (line != null) onProgress?.Invoke(line); - }, true); - - if (errs != null) { - App.RaiseError(errs); - return null; - } - - var path = new DirectoryInfo(folder + "/" + lName).FullName; - var repo = Preference.Instance.AddRepository(path, ""); - return repo; - } - - /// - /// Fetch remote changes - /// - /// - /// - /// - public void Fetch(Remote remote, bool prune, Action onProgress) { - isWatcherDisabled = true; - - var args = "-c credential.helper=manager fetch --progress --verbose "; - - if (prune) args += "--prune "; - - if (remote == null) { - args += "--all"; - } else { - args += remote.Name; - } - - var errs = RunCommand(args, line => { - if (line != null) onProgress?.Invoke(line); - }, true); - - OnSubmoduleChanged?.Invoke(); - - AssertCommand(errs); - } - - /// - /// Pull remote changes. - /// - /// remote - /// branch - /// Progress message handler. - /// Use rebase instead of merge. - /// Auto stash local changes. - /// Progress message handler. - public void Pull(string remote, string branch, Action onProgress, bool rebase = false, bool autostash = false) { - isWatcherDisabled = true; - - var args = "-c credential.helper=manager pull --verbose --progress "; - var needPopStash = false; - - if (rebase) args += "--rebase "; - if (autostash) { - if (rebase) { - args += "--autostash "; - } else { - var changes = LocalChanges(); - if (changes.Count > 0) { - var fatal = RunCommand("stash push -u -m \"PULL_AUTO_STASH\"", null); - if (fatal != null) { - App.RaiseError(fatal); - isWatcherDisabled = false; - return; - } - needPopStash = true; - } - } - } - - var errs = RunCommand(args + remote + " " + branch, line => { - if (line != null) onProgress?.Invoke(line); - }, true); - - OnSubmoduleChanged?.Invoke(); - - AssertCommand(errs); - - if (needPopStash) RunCommand("stash pop -q stash@{0}", null); - } - - /// - /// Push local branch to remote. - /// - /// Remote - /// Local branch name - /// Remote branch name - /// Progress message handler. - /// Push tags - /// Create track reference - /// Force push - public void Push(string remote, string localBranch, string remoteBranch, Action onProgress, bool withTags = false, bool track = false, bool force = false) { - isWatcherDisabled = true; - - var args = "-c credential.helper=manager push --progress --verbose "; - - if (withTags) args += "--tags "; - if (track) args += "-u "; - if (force) args += "--force-with-lease "; - - var errs = RunCommand(args + remote + " " + localBranch + ":" + remoteBranch, line => { - if (line != null) onProgress?.Invoke(line); - }, true); - - AssertCommand(errs); - } - - /// - /// Apply patch. - /// - /// - /// - /// - public void Apply(string patch, bool ignoreSpaceChanges, string whitespaceMode) { - isWatcherDisabled = true; - - var args = "apply "; - if (ignoreSpaceChanges) args += "--ignore-whitespace "; - else args += $"--whitespace={whitespaceMode} "; - - var errs = RunCommand($"{args} \"{patch}\"", null); - if (errs != null) { - App.RaiseError(errs); - } else { - OnWorkingCopyChanged?.Invoke(); - } - - isWatcherDisabled = false; - } - - /// - /// Revert given commit. - /// - /// - /// - public void Revert(string commit, bool autoCommit) { - isWatcherDisabled = true; - - var errs = RunCommand($"revert {commit} --no-edit" + (autoCommit ? "" : " --no-commit"), null); - AssertCommand(errs); - } - - /// - /// Checkout - /// - /// Options. - public void Checkout(string option) { - isWatcherDisabled = true; - - var errs = RunCommand($"checkout {option}", null); - AssertCommand(errs); - } - - /// - /// Merge given branch into current. - /// - /// - /// - public void Merge(string branch, string option) { - isWatcherDisabled = true; - - var errs = RunCommand($"merge {branch} {option}", null); - AssertCommand(errs); - } - - /// - /// Rebase current branch to revision - /// - /// - /// - public void Rebase(string revision, bool autoStash) { - isWatcherDisabled = true; - - var args = $"rebase "; - if (autoStash) args += "--autostash "; - args += revision; - - var errs = RunCommand(args, null); - AssertCommand(errs); - } - - /// - /// Reset. - /// - /// - /// - public void Reset(string revision, string mode = "") { - isWatcherDisabled = true; - - var errs = RunCommand($"reset {mode} {revision}", null); - AssertCommand(errs); - } - - /// - /// Cherry pick commit. - /// - /// - /// - public void CherryPick(string commit, bool noCommit) { - isWatcherDisabled = true; - - var args = "cherry-pick "; - args += noCommit ? "-n " : "--ff "; - args += commit; - - var errs = RunCommand(args, null); - AssertCommand(errs); - } - - /// - /// Stage(add) files to index. - /// - /// - public void Stage(params string[] files) { - isWatcherDisabled = true; - - var args = "add"; - if (files == null || files.Length == 0) { - args += " ."; - } else { - args += " --"; - foreach (var file in files) args += $" \"{file}\""; - } - - var errs = RunCommand(args, null); - if (errs != null) App.RaiseError(errs); - - OnWorkingCopyChanged?.Invoke(); - isWatcherDisabled = false; - } - - /// - /// Unstage files from index - /// - /// - public void Unstage(params string[] files) { - isWatcherDisabled = true; - - var args = "reset"; - if (files != null && files.Length > 0) { - args += " --"; - foreach (var file in files) args += $" \"{file}\""; - } - - var errs = RunCommand(args, null); - if (errs != null) App.RaiseError(errs); - - OnWorkingCopyChanged?.Invoke(); - isWatcherDisabled = false; - } - - /// - /// Discard changes. - /// - /// - public void Discard(List changes) { - isWatcherDisabled = true; - - if (changes == null || changes.Count == 0) { - var errs = RunCommand("reset --hard HEAD", null); - if (errs != null) { - App.RaiseError(errs); - isWatcherDisabled = false; - return; - } - - RunCommand("clean -qfd", null); - } else { - foreach (var change in changes) { - if (change.WorkTree == Change.Status.Untracked || change.WorkTree == Change.Status.Added) { - RunCommand($"clean -qfd -- \"{change.Path}\"", null); - } else { - RunCommand($"checkout -f -- \"{change.Path}\"", null); - } - } - } - - OnWorkingCopyChanged?.Invoke(); - isWatcherDisabled = false; - } - - /// - /// Commit - /// - /// - /// - public bool DoCommit(string message, bool amend) { - isWatcherDisabled = true; - - var file = System.IO.Path.GetTempFileName(); - File.WriteAllText(file, message); - - var args = $"commit --file=\"{file}\""; - if (amend) args += " --amend --no-edit"; - var errs = RunCommand(args, null); - AssertCommand(errs); - - var branch = CurrentBranch(); - OnNavigateCommit?.Invoke(branch.Head); - return string.IsNullOrEmpty(errs); - } - - /// - /// Get all remotes of this repository. - /// - /// Force reload - /// Remote collection - public List Remotes(bool bForceReload = false) { - if (cachedRemotes.Count == 0 || bForceReload) { - cachedRemotes = Remote.Load(this); - } - - return cachedRemotes; - } - - /// - /// Local changes in working copy. - /// - /// Changes. - public List LocalChanges() { - List changes = new List(); - RunCommand("status -uall --ignore-submodules=dirty --porcelain", line => { - if (!string.IsNullOrEmpty(line)) { - var change = Change.Parse(line); - if (change != null) changes.Add(change); - } - }); - return changes; - } - - /// - /// Get total commit count. - /// - /// Number of total commits. - public int TotalCommits() { - int count = 0; - RunCommand("rev-list --all --count", line => { - if (!string.IsNullOrEmpty(line)) count = int.Parse(line.Trim()); - }); - return count; - } - - /// - /// Load commits. - /// - /// Extra limit arguments for `git log` - /// Commit collection - public List Commits(string limit = null) { - return Commit.Load(this, (limit == null ? "" : limit)); ; - } - - /// - /// Load all branches. - /// - /// Force reload. - /// Branches collection. - public List Branches(bool bForceReload = false) { - if (cachedBranches.Count == 0 || bForceReload) { - cachedBranches = Branch.Load(this); - } - - if (IsGitFlowEnabled()) { - foreach (var b in cachedBranches) { - if (b.IsLocal) { - if (b.Name.StartsWith(featurePrefix)) { - b.Kind = Branch.Type.Feature; - } else if (b.Name.StartsWith(releasePrefix)) { - b.Kind = Branch.Type.Release; - } else if (b.Name.StartsWith(hotfixPrefix)) { - b.Kind = Branch.Type.Hotfix; - } - } - } - } - - return cachedBranches; - } - - /// - /// Get current branch - /// - /// - public Branch CurrentBranch() { - foreach (var b in cachedBranches) { - if (b.IsCurrent) return b; - } - - return null; - } - - /// - /// Load all tags. - /// - /// - /// - public List Tags(bool bForceReload = false) { - if (cachedTags.Count == 0 || bForceReload) { - cachedTags = Tag.Load(this); - } - - return cachedTags; - } - - /// - /// Get all stashes - /// - /// - public List Stashes() { - var reflog = new Regex(@"^Reflog: refs/(stash@\{\d+\}).*$"); - var stashes = new List(); - var current = null as Stash; - - var errs = RunCommand("stash list --pretty=raw", line => { - if (line.StartsWith("commit ")) { - if (current != null && !string.IsNullOrEmpty(current.Name)) stashes.Add(current); - current = new Stash() { SHA = line.Substring(7, 8) }; - return; - } - - if (current == null) return; - - if (line.StartsWith("Reflog: refs/stash@")) { - var match = reflog.Match(line); - if (match.Success) current.Name = match.Groups[1].Value; - } else if (line.StartsWith("Reflog message: ")) { - current.Message = line.Substring(16); - } else if (line.StartsWith("author ")) { - current.Author.Parse(line); - } - }); - - if (current != null) stashes.Add(current); - if (errs != null) App.RaiseError(errs); - return stashes; - } - - /// - /// Get all submodules - /// - /// - public List Submodules() { - var test = new Regex(@"^[\-\+ ][0-9a-f]+\s(.*)\(.*\)$"); - var modules = new List(); - - var errs = RunCommand("submodule status", line => { - var match = test.Match(line); - if (!match.Success) return; - - modules.Add(match.Groups[1].Value); - }); - - return modules; - } - - /// - /// Add submodule - /// - /// - /// - /// - /// - public void AddSubmodule(string url, string localPath, bool recursive, Action onProgress) { - isWatcherDisabled = true; - - var errs = RunCommand($"submodule add {url} {localPath}", onProgress, true); - if (errs == null) { - if (recursive) RunCommand($"submodule update --init --recursive -- {localPath}", onProgress, true); - OnWorkingCopyChanged?.Invoke(); - OnSubmoduleChanged?.Invoke(); - } else { - App.RaiseError(errs); - } - - isWatcherDisabled = false; - } - - /// - /// Update submodule. - /// - public void UpdateSubmodule() { - isWatcherDisabled = true; - - var errs = RunCommand("submodule update --rebase --remote", null); - if (errs != null) { - App.RaiseError(errs); - } else { - OnSubmoduleChanged?.Invoke(); - } - - isWatcherDisabled = false; - } - - /// - /// Blame file. - /// - /// - /// - /// - public Blame BlameFile(string file, string revision) { - var regex = new Regex(@"^\^?([0-9a-f]+)\s+.*\((.*)\s+(\d+)\s+[\-\+]?\d+\s+\d+\) (.*)"); - var blame = new Blame(); - var current = null as Blame.Block; - - var errs = RunCommand($"blame -t {revision} -- \"{file}\"", line => { - if (blame.IsBinary) return; - if (string.IsNullOrEmpty(line)) return; - - if (line.IndexOf('\0') >= 0) { - blame.IsBinary = true; - blame.Blocks.Clear(); - return; - } - - var match = regex.Match(line); - if (!match.Success) return; - - var commit = match.Groups[1].Value; - var data = match.Groups[4].Value; - if (current != null && current.CommitSHA == commit) { - current.Content = current.Content + "\n" + data; - } else { - var timestamp = int.Parse(match.Groups[3].Value); - var when = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp).ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"); - - current = new Blame.Block() { - CommitSHA = commit, - Author = match.Groups[2].Value, - Time = when, - Content = data, - }; - - if (current.Author == null) current.Author = ""; - blame.Blocks.Add(current); - } - - blame.LineCount++; - }); - - if (errs != null) App.RaiseError(errs); - return blame; - } - - /// - /// Get file size. - /// - /// - /// - /// - public long GetFileSize(string sha, string path) { - long size = 0; - RunCommand($"cat-file -s {sha}:\"{path}\"", line => { - if (!long.TryParse(line, out size)) size = 0; - }); - return size; - } - #endregion - - #region METHOD_GITFLOW - /// - /// Check if git-flow feature enabled - /// - /// - public bool IsGitFlowEnabled() { - return !string.IsNullOrEmpty(featurePrefix) - && !string.IsNullOrEmpty(releasePrefix) - && !string.IsNullOrEmpty(hotfixPrefix); - } - - /// - /// Get git-flow branch prefix. - /// - /// - public string GetFeaturePrefix() { return featurePrefix; } - public string GetReleasePrefix() { return releasePrefix; } - public string GetHotfixPrefix() { return hotfixPrefix; } - - /// - /// Enable git-flow - /// - /// - /// - /// - /// - /// - /// - public void EnableGitFlow(string master, string develop, string feature, string release, string hotfix, string version = "") { - isWatcherDisabled = true; - - var branches = Branches(); - var masterBranch = branches.Find(b => b.Name == master); - var devBranch = branches.Find(b => b.Name == develop); - var refreshBranches = false; - - if (masterBranch == null) { - var errs = RunCommand($"branch --no-track {master}", null); - if (errs != null) { - App.RaiseError(errs); - isWatcherDisabled = false; - return; - } - - refreshBranches = true; - } - - if (devBranch == null) { - var errs = RunCommand($"branch --no-track {develop}", null); - if (errs != null) { - App.RaiseError(errs); - if (refreshBranches) { - Branches(true); - OnBranchChanged?.Invoke(); - OnCommitsChanged?.Invoke(); - OnWorkingCopyChanged?.Invoke(); - } - isWatcherDisabled = false; - return; - } - - refreshBranches = true; - } - - SetConfig("gitflow.branch.master", master); - SetConfig("gitflow.branch.develop", develop); - SetConfig("gitflow.prefix.feature", feature); - SetConfig("gitflow.prefix.bugfix", "bugfix"); - SetConfig("gitflow.prefix.release", release); - SetConfig("gitflow.prefix.hotfix", hotfix); - SetConfig("gitflow.prefix.support", "support"); - SetConfig("gitflow.prefix.versiontag", version); - - RunCommand("flow init -d", null); - - featurePrefix = GetConfig("gitflow.prefix.feature"); - releasePrefix = GetConfig("gitflow.prefix.release"); - hotfixPrefix = GetConfig("gitflow.prefix.hotfix"); - - if (!IsGitFlowEnabled()) App.RaiseError("Initialize Git-flow failed!"); - - if (refreshBranches) { - Branches(true); - OnBranchChanged?.Invoke(); - OnCommitsChanged?.Invoke(); - OnWorkingCopyChanged?.Invoke(); - } - - isWatcherDisabled = false; - } - - /// - /// Start git-flow branch - /// - /// - /// - public void StartGitFlowBranch(Branch.Type type, string name) { - isWatcherDisabled = true; - - string args; - switch (type) { - case Branch.Type.Feature: args = $"flow feature start {name}"; break; - case Branch.Type.Release: args = $"flow release start {name}"; break; - case Branch.Type.Hotfix: args = $"flow hotfix start {name}"; break; - default: - App.RaiseError("Bad git-flow branch type!"); - return; - } - - var errs = RunCommand(args, null); - AssertCommand(errs); - } - - /// - /// Finish git-flow branch - /// - /// - public void FinishGitFlowBranch(Branch branch) { - isWatcherDisabled = true; - - string args; - switch (branch.Kind) { - case Branch.Type.Feature: - args = $"flow feature finish {branch.Name.Substring(featurePrefix.Length)}"; - break; - case Branch.Type.Release: - var releaseName = branch.Name.Substring(releasePrefix.Length); - args = $"flow release finish {releaseName} -m \"Release done\""; - break; - case Branch.Type.Hotfix: - var hotfixName = branch.Name.Substring(hotfixPrefix.Length); - args = $"flow hotfix finish {hotfixName} -m \"Hotfix done\""; - break; - default: - App.RaiseError("Bad git-flow branch type!"); - return; - } - - var errs = RunCommand(args, null); - AssertCommand(errs); - OnTagChanged?.Invoke(); - } - #endregion - - #region METHOD_COMMITMSG - public void RecordCommitMessage(string message) { - if (string.IsNullOrEmpty(message)) return; - - int exists = CommitMsgRecords.Count; - if (exists > 0) { - var last = CommitMsgRecords[0]; - if (last == message) return; - } - - if (exists >= 10) { - CommitMsgRecords.RemoveRange(9, exists - 9); - } - - CommitMsgRecords.Insert(0, message); - } - #endregion - } -} diff --git a/SourceGit/Git/Stash.cs b/SourceGit/Git/Stash.cs deleted file mode 100644 index d83f85f0..00000000 --- a/SourceGit/Git/Stash.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SourceGit.Git { - - /// - /// Git stash - /// - public class Stash { - - /// - /// SHA for this stash - /// - public string SHA { get; set; } - - /// - /// Name - /// - public string Name { get; set; } - - /// - /// Author - /// - public User Author { get; set; } = new User(); - - /// - /// Message - /// - public string Message { get; set; } - - /// - /// Stash push. - /// - /// - /// - /// - /// - public static void Push(Repository repo, bool includeUntracked, string message, List files) { - string specialFiles = ""; - - if (files.Count > 0) { - specialFiles = " --"; - foreach (var f in files) specialFiles += $" \"{f}\""; - } - - string args = "stash push "; - if (includeUntracked) args += "-u "; - if (!string.IsNullOrEmpty(message)) args += $"-m \"{message}\" "; - - var errs = repo.RunCommand(args + specialFiles, null); - if (errs != null) App.RaiseError(errs); - } - - /// - /// Get changed file list in this stash. - /// - /// - /// - public List GetChanges(Repository repo) { - List changes = new List(); - - var errs = repo.RunCommand($"diff --name-status --pretty=format: {SHA}^ {SHA}", line => { - var change = Change.Parse(line); - if (change != null) changes.Add(change); - }); - - if (errs != null) App.RaiseError(errs); - return changes; - } - - /// - /// Apply stash. - /// - /// - public void Apply(Repository repo) { - var errs = repo.RunCommand($"stash apply -q {Name}", null); - if (errs != null) App.RaiseError(errs); - } - - /// - /// Pop stash - /// - /// - public void Pop(Repository repo) { - var errs = repo.RunCommand($"stash pop -q {Name}", null); - if (errs != null) App.RaiseError(errs); - } - - /// - /// Drop stash - /// - /// - public void Drop(Repository repo) { - var errs = repo.RunCommand($"stash drop -q {Name}", null); - if (errs != null) App.RaiseError(errs); - } - } -} diff --git a/SourceGit/Git/Tag.cs b/SourceGit/Git/Tag.cs deleted file mode 100644 index f329c4d4..00000000 --- a/SourceGit/Git/Tag.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Text.RegularExpressions; - -namespace SourceGit.Git { - - /// - /// Git tag. - /// - public class Tag { - private static readonly Regex FORMAT = new Regex(@"\$(.*)\$(.*)\$(.*)"); - - /// - /// SHA - /// - public string SHA { get; set; } - - /// - /// Display name. - /// - public string Name { get; set; } - - /// - /// Enable filter in log histories. - /// - public bool IsFiltered { get; set; } - - /// - /// Load all tags - /// - /// - /// - public static List Load(Repository repo) { - var args = "for-each-ref --sort=-creatordate --format=\"$%(refname:short)$%(objectname)$%(*objectname)\" refs/tags"; - var tags = new List(); - - repo.RunCommand(args, line => { - var match = FORMAT.Match(line); - if (!match.Success) return; - - var name = match.Groups[1].Value; - var commit = match.Groups[2].Value; - var dereference = match.Groups[3].Value; - - if (string.IsNullOrEmpty(dereference)) { - tags.Add(new Tag() { - Name = name, - SHA = commit, - }); - } else { - tags.Add(new Tag() { - Name = name, - SHA = dereference, - }); - } - }); - - return tags; - } - - /// - /// Add new tag. - /// - /// - /// - /// - /// - public static void Add(Repository repo, string name, string startPoint, string message) { - var args = $"tag -a {name} {startPoint} "; - - if (!string.IsNullOrEmpty(message)) { - string temp = Path.GetTempFileName(); - File.WriteAllText(temp, message); - args += $"-F \"{temp}\""; - } else { - args += $"-m {name}"; - } - - var errs = repo.RunCommand(args, null); - if (errs != null) App.RaiseError(errs); - else repo.OnCommitsChanged?.Invoke(); - } - - /// - /// Delete tag. - /// - /// - /// - /// - public static void Delete(Repository repo, string name, bool push) { - var errs = repo.RunCommand($"tag --delete {name}", null); - if (errs != null) { - App.RaiseError(errs); - return; - } - - if (push) { - var remotes = repo.Remotes(); - foreach (var r in remotes) { - repo.RunCommand($"-c credential.helper=manager push --delete {r.Name} refs/tags/{name}", null); - } - } - - repo.OnCommitsChanged?.Invoke(); - } - - /// - /// Push tag to remote. - /// - /// - /// - /// - public static void Push(Repository repo, string name, string remote) { - var errs = repo.RunCommand($"-c credential.helper=manager push {remote} refs/tags/{name}", null); - if (errs != null) App.RaiseError(errs); - } - } -} diff --git a/SourceGit/Git/User.cs b/SourceGit/Git/User.cs deleted file mode 100644 index 0e1b4120..00000000 --- a/SourceGit/Git/User.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Text.RegularExpressions; - -namespace SourceGit.Git { - - /// - /// Git user. - /// - public class User { - private static readonly Regex FORMAT = new Regex(@"\w+ (.*) <([\w\.\-_]+@[\w\.\-_]+)> (\d{10}) [\+\-]\d+"); - - /// - /// Name. - /// - public string Name { get; set; } = ""; - - /// - /// Email. - /// - public string Email { get; set; } = ""; - - /// - /// Operation time. - /// - public string Time { get; set; } = ""; - - /// - /// Parse user from raw string. - /// - /// Raw string - public void Parse(string data) { - var match = FORMAT.Match(data); - if (!match.Success) return; - - var time = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(int.Parse(match.Groups[3].Value)); - - Name = match.Groups[1].Value; - Email = match.Groups[2].Value; - Time = time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"); - } - } -} diff --git a/SourceGit/Helpers/CommitGraph.cs b/SourceGit/Helpers/CommitGraph.cs deleted file mode 100644 index e2294cab..00000000 --- a/SourceGit/Helpers/CommitGraph.cs +++ /dev/null @@ -1,275 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Windows; -using System.Windows.Media; - -namespace SourceGit.Helpers { - - /// - /// Tools to parse commit graph. - /// - public class CommitGraphMaker { - /// - /// Sizes - /// - public static readonly double UNIT_WIDTH = 12; - public static readonly double HALF_WIDTH = 6; - public static readonly double DOUBLE_WIDTH = 24; - public static readonly double UNIT_HEIGHT = 24; - public static readonly double HALF_HEIGHT = 12; - - /// - /// Colors - /// - public static Brush[] Colors = new Brush[] { - Brushes.Orange, - Brushes.ForestGreen, - Brushes.Gold, - Brushes.Magenta, - Brushes.Red, - Brushes.Gray, - Brushes.Turquoise, - Brushes.Olive, - }; - - /// - /// Helpers to draw lines. - /// - public class LineHelper { - private double lastX = 0; - private double lastY = 0; - - /// - /// Parent commit id. - /// - public string Next { get; set; } - - /// - /// Is merged into this tree. - /// - public bool IsMerged { get; set; } - - /// - /// Points in line - /// - public List Points { get; set; } - - /// - /// Brush to draw line - /// - public Brush Brush { get; set; } - - /// - /// Current horizontal offset. - /// - public double HorizontalOffset => lastX; - - /// - /// Constructor. - /// - /// Parent commit id - /// Is merged in tree - /// Color index - /// Start point - public LineHelper(string nextCommitId, bool isMerged, int colorIdx, Point startPoint) { - Next = nextCommitId; - IsMerged = isMerged; - Points = new List() { startPoint }; - Brush = Colors[colorIdx % Colors.Length]; - - lastX = startPoint.X; - lastY = startPoint.Y; - } - - /// - /// Line to. - /// - /// - /// - /// - public void AddPoint(double x, double y, bool isEnd = false) { - if (x > lastX) { - Points.Add(new Point(lastX, lastY)); - Points.Add(new Point(x, y - HALF_HEIGHT)); - } else if (x < lastX) { - Points.Add(new Point(lastX, lastY + HALF_HEIGHT)); - Points.Add(new Point(x, y)); - } - - lastX = x; - lastY = y; - - if (isEnd) { - var last = Points.Last(); - if (last.X != lastX || last.Y != lastY) Points.Add(new Point(lastX, lastY)); - } - } - } - - /// - /// Short link between two commits. - /// - public struct ShortLink { - public Point Start; - public Point Control; - public Point End; - public Brush Brush; - } - - /// - /// Dot - /// - public struct Dot { - public double X; - public double Y; - public Brush Color; - } - - /// - /// Independent lines in graph - /// - public List Lines { get; set; } = new List(); - - /// - /// Short links. - /// - public List Links { get; set; } = new List(); - - /// - /// All dots. - /// - public List Dots { get; set; } = new List(); - - /// - /// Highlight commit id. - /// - public string Highlight { get; set; } - - /// - /// Parse commits. - /// - /// - /// - public static CommitGraphMaker Parse(List commits) { - CommitGraphMaker maker = new CommitGraphMaker(); - - List unsolved = new List(); - List ended = new List(); - Dictionary currentMap = new Dictionary(); - double offsetY = -HALF_HEIGHT; - int colorIdx = 0; - - for (int i = 0; i < commits.Count; i++) { - Git.Commit commit = commits[i]; - LineHelper major = null; - bool isMerged = commit.IsHEAD || commit.IsMerged; - int oldCount = unsolved.Count; - - // 更新Y坐标 - offsetY += UNIT_HEIGHT; - - // 找到当前的分支的HEAD,用于默认选中 - if (maker.Highlight == null && commit.IsHEAD) { - maker.Highlight = commit.SHA; - } - - // 找到第一个依赖于本提交的树,将其他依赖于本提交的树标记为终止,并对已存在的线路调整(防止线重合) - double offsetX = -HALF_WIDTH; - foreach (var l in unsolved) { - if (l.Next == commit.SHA) { - if (major == null) { - offsetX += UNIT_WIDTH; - major = l; - - if (commit.Parents.Count > 0) { - major.Next = commit.Parents[0]; - if (!currentMap.ContainsKey(major.Next)) currentMap.Add(major.Next, major); - } else { - major.Next = "ENDED"; - ended.Add(l); - } - - major.AddPoint(offsetX, offsetY); - } else { - ended.Add(l); - } - - isMerged = isMerged || l.IsMerged; - } else { - if (!currentMap.ContainsKey(l.Next)) currentMap.Add(l.Next, l); - offsetX += UNIT_WIDTH; - l.AddPoint(offsetX, offsetY); - } - } - - // 处理本提交为非当前分支HEAD的情况(创建新依赖线路) - if (major == null && commit.Parents.Count > 0) { - offsetX += UNIT_WIDTH; - major = new LineHelper(commit.Parents[0], isMerged, colorIdx, new Point(offsetX, offsetY)); - unsolved.Add(major); - colorIdx++; - } - - // 确定本提交的点的位置 - Point position = new Point(offsetX, offsetY); - if (major != null) { - major.IsMerged = isMerged; - position.X = major.HorizontalOffset; - position.Y = offsetY; - maker.Dots.Add(new Dot() { X = position.X - 3, Y = position.Y - 3, Color = major.Brush }); - } else { - maker.Dots.Add(new Dot() { X = position.X - 3, Y = position.Y - 3, Color = Brushes.Orange }); - } - - // 处理本提交的其他依赖 - for (int j = 1; j < commit.Parents.Count; j++) { - var parent = commit.Parents[j]; - if (currentMap.ContainsKey(parent)) { - var l = currentMap[parent]; - var link = new ShortLink(); - - link.Start = position; - link.End = new Point(l.HorizontalOffset, offsetY + HALF_HEIGHT); - link.Control = new Point(link.End.X, link.Start.Y); - link.Brush = l.Brush; - maker.Links.Add(link); - } else { - offsetX += UNIT_WIDTH; - unsolved.Add(new LineHelper(commit.Parents[j], isMerged, colorIdx, position)); - colorIdx++; - } - } - - // 处理已终止的线 - foreach (var l in ended) { - l.AddPoint(position.X, position.Y, true); - maker.Lines.Add(l); - unsolved.Remove(l); - } - - // 加入本次提交 - commit.IsMerged = isMerged; - commit.GraphOffset = System.Math.Max(offsetX + HALF_WIDTH, oldCount * UNIT_WIDTH); - - // 清理临时数据 - ended.Clear(); - currentMap.Clear(); - } - - // 处理尚未终结的线 - for (int i = 0; i < unsolved.Count; i++) { - var path = unsolved[i]; - path.AddPoint((i + 0.5) * UNIT_WIDTH, (commits.Count - 0.5) * UNIT_HEIGHT, true); - maker.Lines.Add(path); - } - unsolved.Clear(); - - // 处理默认选中异常 - if (maker.Highlight == null && commits.Count > 0) { - maker.Highlight = commits[0].SHA; - } - - return maker; - } - } -} diff --git a/SourceGit/Helpers/TextBoxHelper.cs b/SourceGit/Helpers/TextBoxHelper.cs deleted file mode 100644 index 9e28c20f..00000000 --- a/SourceGit/Helpers/TextBoxHelper.cs +++ /dev/null @@ -1,224 +0,0 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using System.Windows.Media; - -namespace SourceGit.Helpers { - - /// - /// Attached properties to TextBox. - /// - public static class TextBoxHelper { - - /// - /// Auto scroll on text changed or selection changed. - /// - public static readonly DependencyProperty AutoScrollProperty = DependencyProperty.RegisterAttached( - "AutoScroll", - typeof(bool), - typeof(TextBoxHelper), - new PropertyMetadata(false, OnAutoScrollChanged)); - - /// - /// Placeholder property - /// - public static readonly DependencyProperty PlaceholderProperty = DependencyProperty.RegisterAttached( - "Placeholder", - typeof(string), - typeof(TextBoxHelper), - new PropertyMetadata(string.Empty, OnPlaceholderChanged)); - - /// - /// Vertical alignment for placeholder. - /// - public static readonly DependencyProperty PlaceholderBaselineProperty = DependencyProperty.RegisterAttached( - "PlaceholderBaseline", - typeof(AlignmentY), - typeof(TextBoxHelper), - new PropertyMetadata(AlignmentY.Center)); - - /// - /// Property to store generated placeholder brush. - /// - public static readonly DependencyProperty PlaceholderBrushProperty = DependencyProperty.RegisterAttached( - "PlaceholderBrush", - typeof(Brush), - typeof(TextBoxHelper), - new PropertyMetadata(Brushes.Transparent)); - - /// - /// Setter for AutoScrollProperty - /// - /// - /// - public static void SetAutoScroll(UIElement element, bool enabled) { - element.SetValue(AutoScrollProperty, enabled); - } - - /// - /// Getter for AutoScrollProperty - /// - /// - /// - public static bool GetAutoScroll(UIElement element) { - return (bool)element.GetValue(AutoScrollProperty); - } - - /// - /// Triggered when AutoScroll property changed. - /// - /// - /// - public static void OnAutoScrollChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - var textBox = d as TextBox; - if (textBox == null) return; - - textBox.SelectionChanged -= UpdateScrollOnSelectionChanged; - if ((bool)e.NewValue == true) { - textBox.SelectionChanged += UpdateScrollOnSelectionChanged; - } - } - - /// - /// Triggered when placeholder changed. - /// - /// - /// - private static void OnPlaceholderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - var textBox = d as TextBox; - if (textBox != null) textBox.Loaded += OnTextLoaded; - } - - /// - /// Setter for Placeholder property - /// - /// - /// - public static void SetPlaceholder(UIElement element, string value) { - element.SetValue(PlaceholderProperty, value); - } - - /// - /// Getter for Placeholder property - /// - /// - /// - public static string GetPlaceholder(UIElement element) { - return (string)element.GetValue(PlaceholderProperty); - } - - /// - /// Setter for PlaceholderBaseline property - /// - /// - /// - public static void SetPlaceholderBaseline(UIElement element, AlignmentY align) { - element.SetValue(PlaceholderBaselineProperty, align); - } - - /// - /// Setter for PlaceholderBaseline property. - /// - /// - /// - public static AlignmentY GetPlaceholderBaseline(UIElement element) { - return (AlignmentY)element.GetValue(PlaceholderBaselineProperty); - } - - /// - /// Setter for PlaceholderBrush property. - /// - /// - /// - public static void SetPlaceholderBrush(UIElement element, Brush value) { - element.SetValue(PlaceholderBrushProperty, value); - } - - /// - /// Getter for PlaceholderBrush property. - /// - /// - /// - public static Brush GetPlaceholderBrush(UIElement element) { - return (Brush)element.GetValue(PlaceholderBrushProperty); - } - - /// - /// Set placeholder as background when TextBox was loaded. - /// - /// - /// - private static void OnTextLoaded(object sender, RoutedEventArgs e) { - var textBox = sender as TextBox; - if (textBox == null) return; - - Label placeholder = new Label(); - placeholder.Content = textBox.GetValue(PlaceholderProperty); - - VisualBrush brush = new VisualBrush(); - brush.AlignmentX = AlignmentX.Left; - brush.AlignmentY = GetPlaceholderBaseline(textBox); - brush.TileMode = TileMode.None; - brush.Stretch = Stretch.None; - brush.Opacity = 0.3; - brush.Visual = placeholder; - - textBox.SetValue(PlaceholderBrushProperty, brush); - textBox.Background = brush; - textBox.TextChanged += UpdatePlaceholder; - UpdatePlaceholder(textBox, null); - } - - /// - /// Dynamically hide/show placeholder. - /// - /// - /// - private static void UpdatePlaceholder(object sender, RoutedEventArgs e) { - var textBox = sender as TextBox; - if (string.IsNullOrEmpty(textBox.Text)) { - textBox.Background = textBox.GetValue(PlaceholderBrushProperty) as Brush; - } else { - textBox.Background = Brushes.Transparent; - } - } - - /// - /// - /// - /// - /// - private static void UpdateScrollOnSelectionChanged(object sender, RoutedEventArgs e) { - var textBox = sender as TextBox; - if (textBox != null && textBox.IsFocused) { - if (Mouse.LeftButton == MouseButtonState.Pressed && textBox.SelectionLength > 0) { - var p = Mouse.GetPosition(textBox); - if (p.X <= 8) { - textBox.LineLeft(); - } else if (p.X >= textBox.ActualWidth - 8) { - textBox.LineRight(); - } - - if (p.Y <= 8) { - textBox.LineUp(); - } else if (p.Y >= textBox.ActualHeight - 8) { - textBox.LineDown(); - } - } else { - var rect = textBox.GetRectFromCharacterIndex(textBox.CaretIndex); - if (rect.Left <= 0) { - textBox.ScrollToHorizontalOffset(textBox.HorizontalOffset + rect.Left); - } else if (rect.Right >= textBox.ActualWidth) { - textBox.ScrollToHorizontalOffset(textBox.HorizontalOffset + rect.Right); - } - - if (rect.Top <= 0) { - textBox.ScrollToVerticalOffset(textBox.VerticalOffset + rect.Top); - } else if (rect.Bottom >= textBox.ActualHeight) { - textBox.ScrollToVerticalOffset(textBox.VerticalOffset + rect.Bottom); - } - } - } - } - } -} diff --git a/SourceGit/Helpers/TreeViewHelper.cs b/SourceGit/Helpers/TreeViewHelper.cs deleted file mode 100644 index d1b74bf2..00000000 --- a/SourceGit/Helpers/TreeViewHelper.cs +++ /dev/null @@ -1,329 +0,0 @@ -using System.Collections.ObjectModel; -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using System.Windows.Media; - -namespace SourceGit.Helpers { - - /// - /// Helper class to enable multi-selection of TreeView - /// - public static class TreeViewHelper { - - /// - /// Definition of EnableMultiSelection property. - /// - public static readonly DependencyProperty EnableMultiSelectionProperty = - DependencyProperty.RegisterAttached( - "EnableMultiSelection", - typeof(bool), - typeof(TreeViewHelper), - new FrameworkPropertyMetadata(false, OnEnableMultiSelectionChanged)); - - /// - /// Getter of EnableMultiSelection - /// - /// - /// - public static bool GetEnableMultiSelection(DependencyObject obj) { - return (bool)obj.GetValue(EnableMultiSelectionProperty); - } - - /// - /// Setter of EnableMultiSelection - /// - /// - /// - public static void SetEnableMultiSelection(DependencyObject obj, bool value) { - obj.SetValue(EnableMultiSelectionProperty, value); - } - - /// - /// Definition of SelectedItems - /// - public static readonly DependencyProperty SelectedItemsProperty = - DependencyProperty.RegisterAttached( - "SelectedItems", - typeof(ObservableCollection), - typeof(TreeViewHelper), - new FrameworkPropertyMetadata(null)); - - /// - /// Getter of SelectedItems - /// - /// - /// - public static ObservableCollection GetSelectedItems(DependencyObject obj) { - return (ObservableCollection)obj.GetValue(SelectedItemsProperty); - } - - /// - /// Setter of SelectedItems - /// - /// - /// - public static void SetSelectedItems(DependencyObject obj, ObservableCollection value) { - obj.SetValue(SelectedItemsProperty, value); - } - - /// - /// Definition of IsChecked property. - /// - public static readonly DependencyProperty IsCheckedProperty = - DependencyProperty.RegisterAttached( - "IsChecked", - typeof(bool), - typeof(TreeViewHelper), - new FrameworkPropertyMetadata(false)); - - /// - /// Getter of IsChecked Property. - /// - /// - /// - public static bool GetIsChecked(DependencyObject obj) { - return (bool)obj.GetValue(IsCheckedProperty); - } - - /// - /// Setter of IsChecked property - /// - /// - /// - public static void SetIsChecked(DependencyObject obj, bool value) { - obj.SetValue(IsCheckedProperty, value); - } - - /// - /// Definition of MultiSelectionChangedEvent - /// - public static readonly RoutedEvent MultiSelectionChangedEvent = - EventManager.RegisterRoutedEvent("MultiSelectionChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TreeViewHelper)); - - /// - /// Add handler for MultiSelectionChanged event. - /// - /// - /// - public static void AddMultiSelectionChangedHandler(DependencyObject d, RoutedEventHandler handler) { - var tree = d as TreeView; - if (tree != null) tree.AddHandler(MultiSelectionChangedEvent, handler); - } - - /// - /// Remove handler for MultiSelectionChanged event. - /// - /// - /// - public static void RemoveMultiSelectionChangedHandler(DependencyObject d, RoutedEventHandler handler) { - var tree = d as TreeView; - if (tree != null) tree.RemoveHandler(MultiSelectionChangedEvent, handler); - } - - /// - /// Select all items in tree. - /// - /// - public static void SelectWholeTree(TreeView tree) { - var selected = GetSelectedItems(tree); - selected.Clear(); - SelectAll(selected, tree); - tree.RaiseEvent(new RoutedEventArgs(MultiSelectionChangedEvent)); - } - - /// - /// Selected one item by DataContext - /// - /// - /// - public static void SelectOneByContext(TreeView tree, object obj) { - var item = FindTreeViewItemByDataContext(tree, obj); - if (item != null) { - var selected = GetSelectedItems(tree); - selected.Add(item); - item.SetValue(IsCheckedProperty, true); - tree.RaiseEvent(new RoutedEventArgs(MultiSelectionChangedEvent)); - } - } - - /// - /// Unselect the whole tree. - /// - /// - public static void UnselectTree(TreeView tree) { - var selected = GetSelectedItems(tree); - if (selected.Count == 0) return; - - foreach (var old in selected) old.SetValue(IsCheckedProperty, false); - selected.Clear(); - tree.RaiseEvent(new RoutedEventArgs(MultiSelectionChangedEvent)); - } - - /// - /// Hooks when EnableMultiSelection changed. - /// - /// - /// - private static void OnEnableMultiSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - var tree = d as TreeView; - if (tree != null && (bool)e.NewValue) { - tree.SetValue(SelectedItemsProperty, new ObservableCollection()); - tree.PreviewMouseDown += OnTreeMouseDown; - } - } - - /// - /// Preview mouse button select. - /// - /// - /// - private static void OnTreeMouseDown(object sender, MouseButtonEventArgs e) { - var tree = sender as TreeView; - if (tree == null) return; - - var hit = VisualTreeHelper.HitTest(tree, e.GetPosition(tree)); - if (hit == null || hit.VisualHit is null) return; - - var item = FindTreeViewItem(hit.VisualHit as UIElement); - if (item == null) return; - - var selected = GetSelectedItems(tree); - if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) { - if (GetIsChecked(item)) { - selected.Remove(item); - item.SetValue(IsCheckedProperty, false); - } else { - selected.Add(item); - item.SetValue(IsCheckedProperty, true); - } - } else if ((Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) && selected.Count > 0) { - var last = selected.Last(); - if (last == item) return; - - var lastPos = last.PointToScreen(new Point(0, 0)); - var curPos = item.PointToScreen(new Point(0, 0)); - if (lastPos.Y > curPos.Y) { - SelectRange(selected, tree, item, last); - } else { - SelectRange(selected, tree, last, item); - } - - selected.Add(item); - item.SetValue(IsCheckedProperty, true); - } else if (e.RightButton == MouseButtonState.Pressed) { - if (GetIsChecked(item)) return; - - foreach (var old in selected) old.SetValue(IsCheckedProperty, false); - selected.Clear(); - selected.Add(item); - item.SetValue(IsCheckedProperty, true); - } else { - foreach (var old in selected) old.SetValue(IsCheckedProperty, false); - selected.Clear(); - selected.Add(item); - item.SetValue(IsCheckedProperty, true); - } - - tree.RaiseEvent(new RoutedEventArgs(MultiSelectionChangedEvent)); - } - - /// - /// Find TreeViewItem by child element. - /// - /// - /// - /// - private static TreeViewItem FindTreeViewItem(DependencyObject child) { - if (child == null) return null; - if (child is TreeViewItem) return child as TreeViewItem; - if (child is TreeView) return null; - return FindTreeViewItem(VisualTreeHelper.GetParent(child)); - } - - /// - /// Find TreeViewItem by DataContext - /// - /// - /// - /// - private static TreeViewItem FindTreeViewItemByDataContext(ItemsControl control, object obj) { - if (control == null) return null; - if (control.DataContext == obj) return control as TreeViewItem; - - for (int i = 0; i < control.Items.Count; i++) { - var child = control.ItemContainerGenerator.ContainerFromIndex(i) as ItemsControl; - var found = FindTreeViewItemByDataContext(child, obj); - if (found != null) return found; - } - - return null; - } - - /// - /// Select all items. - /// - /// - /// - private static void SelectAll(ObservableCollection selected, ItemsControl control) { - for (int i = 0; i < control.Items.Count; i++) { - var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem; - if (child == null) continue; - - selected.Add(child); - child.SetValue(IsCheckedProperty, true); - SelectAll(selected, child); - } - } - - /// - /// Select range items between given. - /// - /// - /// - /// - /// - /// - private static int SelectRange(ObservableCollection selected, ItemsControl control, TreeViewItem from, TreeViewItem to, int matches = 0) { - for (int i = 0; i < control.Items.Count; i++) { - var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem; - if (child == null) continue; - - if (matches == 1) { - if (child == to) return 2; - selected.Add(child); - child.SetValue(IsCheckedProperty, true); - if (TryEndRangeSelection(selected, child, to)) return 2; - } else if (child == from) { - matches = 1; - if (TryEndRangeSelection(selected, child, to)) return 2; - } else { - matches = SelectRange(selected, child, from, to, matches); - if (matches == 2) return 2; - } - } - - return matches; - } - - private static bool TryEndRangeSelection(ObservableCollection selected, TreeViewItem control, TreeViewItem end) { - for (int i = 0; i < control.Items.Count; i++) { - var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem; - if (child == null) continue; - - if (child == end) { - return true; - } else { - selected.Add(child); - child.SetValue(IsCheckedProperty, true); - - var ended = TryEndRangeSelection(selected, child, end); - if (ended) return true; - } - } - - return false; - } - } -} \ No newline at end of file diff --git a/SourceGit/Helpers/Validations.cs b/SourceGit/Helpers/Validations.cs deleted file mode 100644 index a92425b7..00000000 --- a/SourceGit/Helpers/Validations.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System.Globalization; -using System.IO; -using System.Text.RegularExpressions; -using System.Windows.Controls; - -namespace SourceGit.Helpers { - - /// - /// Validate clone folder. - /// - public class CloneFolderRule : ValidationRule { - public override ValidationResult Validate(object value, CultureInfo cultureInfo) { - var badPath = "EXISTS and FULL ACCESS CONTROL needed"; - var path = value as string; - return Directory.Exists(path) ? ValidationResult.ValidResult : new ValidationResult(false, badPath); - } - } - - /// - /// Validate git remote URL - /// - public class RemoteUriRule : ValidationRule { - public override ValidationResult Validate(object value, CultureInfo cultureInfo) { - var badUrl = "Remote git URL not supported"; - return Git.Repository.IsValidUrl(value as string) ? ValidationResult.ValidResult : new ValidationResult(false, badUrl); - } - } - - /// - /// Validate tag name. - /// - public class RemoteNameRule : ValidationRule { - public Git.Repository Repo { get; set; } - - public override ValidationResult Validate(object value, CultureInfo cultureInfo) { - var regex = new Regex(@"^[\w\-\.]+$"); - var name = value as string; - var remotes = Repo.Remotes(); - - if (string.IsNullOrEmpty(name)) return new ValidationResult(false, "Remote name can NOT be null"); - if (!regex.IsMatch(name)) return new ValidationResult(false, $"Bad name for remote. Regex: ^[\\w\\-\\.]+$"); - - foreach (var t in remotes) { - if (t.Name == name) { - return new ValidationResult(false, $"Remote '{name}' already exists"); - } - } - - return ValidationResult.ValidResult; - } - } - - /// - /// Validate branch name. - /// - public class BranchNameRule : ValidationRule { - public Git.Repository Repo { get; set; } - public string Prefix { get; set; } = ""; - - public override ValidationResult Validate(object value, CultureInfo cultureInfo) { - var regex = new Regex(@"^[\w\-/\.]+$"); - var name = value as string; - var branches = Repo.Branches(); - - if (string.IsNullOrEmpty(name)) return new ValidationResult(false, "Branch name can NOT be null"); - if (!regex.IsMatch(name)) return new ValidationResult(false, $"Bad name for branch. Regex: ^[\\w\\-/\\.]+$"); - - name = Prefix + name; - - foreach (var b in branches) { - if (b.Name == name) { - return new ValidationResult(false, $"Branch '{name}' already exists"); - } - } - - return ValidationResult.ValidResult; - } - } - - /// - /// Validate tag name. - /// - public class TagNameRule : ValidationRule { - public Git.Repository Repo { get; set; } - - public override ValidationResult Validate(object value, CultureInfo cultureInfo) { - var regex = new Regex(@"^[\w\-\.]+$"); - var name = value as string; - var tags = Repo.Tags(); - - if (string.IsNullOrEmpty(name)) return new ValidationResult(false, "Tag name can NOT be null"); - if (!regex.IsMatch(name)) return new ValidationResult(false, $"Bad name for tag. Regex: ^[\\w\\-\\.]+$"); - - foreach (var t in tags) { - if (t.Name == name) { - return new ValidationResult(false, $"Tag '{name}' already exists"); - } - } - - return ValidationResult.ValidResult; - } - } - - /// - /// Required for commit subject. - /// - public class CommitSubjectRequiredRule : ValidationRule { - public override ValidationResult Validate(object value, CultureInfo cultureInfo) { - var subject = value as string; - return string.IsNullOrWhiteSpace(subject) ? new ValidationResult(false, "Commit subject can NOT be empty") : ValidationResult.ValidResult; - } - } - - /// - /// Required for patch file. - /// - public class PatchFileRequiredRule : ValidationRule { - public override ValidationResult Validate(object value, CultureInfo cultureInfo) { - var path = value as string; - var succ = !string.IsNullOrEmpty(path) && File.Exists(path); - return !succ ? new ValidationResult(false, "Invalid path for patch file") : ValidationResult.ValidResult; - } - } - - /// - /// Required for submodule path. - /// - public class SubmodulePathRequiredRule : ValidationRule { - public override ValidationResult Validate(object value, CultureInfo cultureInfo) { - var regex = new Regex(@"^[\w\-\._/]+$"); - var path = value as string; - var succ = !string.IsNullOrEmpty(path) && regex.IsMatch(path.Trim()); - return !succ ? new ValidationResult(false, "Invalid path for submodules") : ValidationResult.ValidResult; - } - } -} diff --git a/SourceGit/Properties/AssemblyInfo.cs b/SourceGit/Properties/AssemblyInfo.cs deleted file mode 100644 index c0ea722f..00000000 --- a/SourceGit/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Windows; - -[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] \ No newline at end of file diff --git a/SourceGit/Resources/Controls.xaml b/SourceGit/Resources/Controls.xaml deleted file mode 100644 index 03450990..00000000 --- a/SourceGit/Resources/Controls.xaml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Icons.xaml b/SourceGit/Resources/Icons.xaml deleted file mode 100644 index 66103f5a..00000000 --- a/SourceGit/Resources/Icons.xaml +++ /dev/null @@ -1,61 +0,0 @@ - - M1004.824 466.4L557.72 19.328c-25.728-25.76-67.488-25.76-93.28 0L360.568 123.2l78.176 78.176c12.544-5.984 26.56-9.376 41.376-9.376 53.024 0 96 42.976 96 96 0 14.816-3.36 28.864-9.376 41.376l127.968 127.968c12.544-5.984 26.56-9.376 41.376-9.376 53.024 0 96 42.976 96 96s-42.976 96-96 96-96-42.976-96-96c0-14.816 3.36-28.864 9.376-41.376L521.496 374.624a88.837 88.837 0 0 1-9.376 3.872v266.976c37.28 13.184 64 48.704 64 90.528 0 53.024-42.976 96-96 96s-96-42.976-96-96c0-41.792 26.72-77.344 64-90.528V378.496c-37.28-13.184-64-48.704-64-90.528 0-14.816 3.36-28.864 9.376-41.376l-78.176-78.176L19.416 464.288c-25.76 25.792-25.76 67.52 0 93.28l447.136 447.072c25.728 25.76 67.488 25.76 93.28 0l444.992-444.992c25.76-25.76 25.76-67.552 0-93.28z - M557.696 545.347L789.873 402.66c23.998-14.999 31.297-46.496 16.398-70.493-14.798-23.798-45.995-31.197-69.993-16.699L506.501 456.555 277.123 315.37c-24.098-14.798-55.595-7.3-70.493 16.799-14.799 24.097-7.3 55.594 16.798 70.493l231.778 142.586V819.12c0 28.297 22.897 51.195 51.195 51.195 28.297 0 51.195-22.898 51.195-51.195V545.347h0.1zM506.5 0l443.356 255.975v511.95L506.501 1023.9 63.144 767.925v-511.95L506.5 0z - - M753.613 996.727L269.38 511.505 754.602 27.272z - M270.387 27.273L754.62 512.495 269.398 996.728z - - F1M0,6L0,9 9,9 9,6 0,6z - F1M0,0L0,9 9,9 9,0 0,0 0,3 8,3 8,8 1,8 1,3z - F1M0,10L0,3 3,3 3,0 10,0 10,2 4,2 4,3 7,3 7,6 6,6 6,5 1,5 1,10z M1,10L7,10 7,7 10,7 10,2 9,2 9,6 6,6 6,9 1,9z - M810.666667 273.493333L750.506667 213.333333 512 451.84 273.493333 213.333333 213.333333 273.493333 451.84 512 213.333333 750.506667 273.493333 810.666667 512 572.16 750.506667 810.666667 810.666667 750.506667 572.16 512z - M512 597.33333332m-1.26648097 0a1.26648097 1.26648097 0 1 0 2.53296194 0 1.26648097 1.26648097 0 1 0-2.53296194 0ZM809.691429 392.777143L732.16 314.514286 447.634286 599.771429 292.571429 443.977143 214.308571 521.508571l155.794286 155.794286 77.531429 77.531429 362.057143-362.057143z - M511.680999 0C233.071131 0 6.524722 222.580887 0.12872 499.655715 6.013042 257.886821 189.834154 63.960025 415.740962 63.960025c229.61649 0 415.740162 200.450718 415.740162 447.720175 0 52.958901 42.981137 95.940037 95.940038 95.940037s95.940037-42.981137 95.940037-95.940037c0-282.57539-229.104809-511.6802-511.6802-511.6802z m0 1023.3604c278.609869 0 505.156277-222.580887 511.55228-499.655715-5.884322 241.768894-189.705434 435.69569-415.612242 435.69569-229.61649 0-415.740162-200.450718-415.740163-447.720175 0-52.958901-42.981137-95.940037-95.940037-95.940038s-95.940037 42.981137-95.940037 95.940038c0 282.57539 229.104809 511.6802 511.680199 511.6802z - M701.9062029 677.41589899L589.90712068 565.41681675a148.33953321 148.33953321 0 1 0-24.97646381 26.55648342L676.07895931 703.12160261z m-346.38891409-199.50786053a114.97681148 114.97681148 0 1 1 114.85527151 114.97681148A115.09835147 115.09835147 0 0 1 355.45651882 477.90803846z - M352 64h320L960 352v320L672 960h-320L64 672v-320L352 64z m161.28 362.688L344.128 256 259.584 341.312 428.736 512l-169.152 170.688L344.128 768 513.28 597.312 682.432 768l84.544-85.312L597.824 512l169.152-170.688L682.432 256 513.28 426.688z - - M51.2 204.8h102.4v102.4H51.2V204.8z m204.8 0h716.8v102.4H256V204.8zM51.2 460.8h102.4v102.4H51.2V460.8z m204.8 0h716.8v102.4H256V460.8z m-204.8 256h102.4v102.4H51.2v-102.4z m204.8 0h716.8v102.4H256v-102.4z - M912 737l0 150L362 887l0-100 0-50 0-150 0-150 0-150L112 287l0-150 450 0 0 150L412 287l0 150L912 437l0 150L412 587l0 150L912 737z - - M868 545.5L536.1 163c-12.7-14.7-35.5-14.7-48.3 0L156 545.5c-4.5 5.2-0.8 13.2 6 13.2h81c4.6 0 9-2 12.1-5.5L474 300.9V864c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V300.9l218.9 252.3c3 3.5 7.4 5.5 12.1 5.5h81c6.8 0 10.5-8 6-13.2z - M862 465.3h-81c-4.6 0-9 2-12.1 5.5L550 723.1V160c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v563.1L255.1 470.8c-3-3.5-7.4-5.5-12.1-5.5h-81c-6.8 0-10.5 8.1-6 13.2L487.9 861c12.7 14.7 35.5 14.7 48.3 0L868 478.5c4.5-5.2 0.8-13.2-6-13.2z - - M509.44 546.304l270.848-270.912 90.56 90.56-347.52 349.056-0.832-0.768-13.056 13.056-362.624-361.28 91.136-91.264z - M256 224l1e-8 115.2L512 544l255.99999999-204.8 1e-8-115.2-256 204.80000001L256 224zM512 684.8l-256-204.8L256 595.2 512 800 768 595.2l0-115.2L512 684.8z - M169.5 831l342.8-341.9L855.1 831l105.3-105.3-448.1-448.1L64.2 725.7 169.5 831z - M768 800V684.8L512 480 256 684.8V800l256-204.8L768 800zM512 339.2L768 544V428.8L512 224 256 428.8V544l256-204.8z - - M64.2 180.3h418.2v120.6H64.2zM64.2 461.7h358.5v120.6H64.2zM64.2 723.1h418.2v120.6H64.2zM601.9 180.3h358.5v120.6H601.9zM482.4 119.9h179.2v241.3H482.4zM303.2 401.4h179.2v241.3H303.2zM482.4 662.8h179.2v241.3H482.4zM540.3 461.7h420.1v120.6H540.3zM601.9 723.1h358.5v120.6H601.9z - M887 576.8v-129.4L796.6 418c-4.6-14-10.2-27.4-16.8-40.4l43.2-84.8-91.6-91.6-84.8 43.2c-13-6.6-26.6-12.2-40.4-16.8l-29.4-90.4h-129.4L418 227.6c-13.8 4.6-27.4 10.2-40.4 16.8l-84.8-43.2-91.6 91.6 43.2 84.8c-6.6 13-12.2 26.6-16.8 40.4l-90.4 29.4v129.4l90.4 29.4c4.6 13.8 10.2 27.4 16.8 40.4l-43.2 84.8 91.6 91.6 84.8-43.2c13 6.6 26.6 12.2 40.4 16.8l29.4 90.4h129.4l29.4-90.4c14-4.6 27.4-10.2 40.4-16.8l84.8 43.2 91.6-91.6-43.2-84.8c6.6-13 12.2-26.6 16.8-40.4l90.4-29.4zM512 662c-82.8 0-150-67.2-150-150s67.2-150 150-150 150 67.2 150 150-67.2 150-150 150z - M 38,19C 48.4934,19 57,27.5066 57,38C 57,48.4934 48.4934,57 38,57C 27.5066,57 19,48.4934 19,38C 19,27.5066 27.5066,19 38,19 Z M 33.25,33.25L 33.25,36.4167L 36.4166,36.4167L 36.4166,47.5L 33.25,47.5L 33.25,50.6667L 44.3333,50.6667L 44.3333,47.5L 41.1666,47.5L 41.1666,36.4167L 41.1666,33.25L 33.25,33.25 Z M 38.7917,25.3333C 37.48,25.3333 36.4167,26.3967 36.4167,27.7083C 36.4167,29.02 37.48,30.0833 38.7917,30.0833C 40.1033,30.0833 41.1667,29.02 41.1667,27.7083C 41.1667,26.3967 40.1033,25.3333 38.7917,25.3333 Z - M64 864h896V288h-396.224a64 64 0 0 1-57.242667-35.376L460.224 160H64v704z m-64 32V128a32 32 0 0 1 32-32h448a32 32 0 0 1 28.624 17.690667L563.776 224H992a32 32 0 0 1 32 32v640a32 32 0 0 1-32 32H32a32 32 0 0 1-32-32z - M448 64l128 128h448v768H0V64z - M832 960l192-512H192L0 960zM128 384L0 960V128h288l128 128h416v128z - M958.656 320H960v639.936A64 64 0 0 1 896.128 1024H191.936A63.872 63.872 0 0 1 128 959.936V64.064A64 64 0 0 1 191.936 0H640v320.96h319.616L958.656 320zM320 544c0 17.152 14.464 32 32.192 32h383.552A32.384 32.384 0 0 0 768 544c0-17.152-14.464-32-32.256-32H352.192A32.448 32.448 0 0 0 320 544z m0 128c0 17.152 14.464 32 32.192 32h383.552a32.384 32.384 0 0 0 32.256-32c0-17.152-14.464-32-32.256-32H352.192a32.448 32.448 0 0 0-32.192 32z m0 128c0 17.152 14.464 32 32.192 32h383.552a32.384 32.384 0 0 0 32.256-32c0-17.152-14.464-32-32.256-32H352.192a32.448 32.448 0 0 0-32.192 32z - M854.2 306.6L611.3 72.9c-6-5.7-13.9-8.9-22.2-8.9H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h277l219 210.6V824c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V329.6c0-8.7-3.5-17-9.8-23zM553.4 201.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v704c0 17.7 14.3 32 32 32h512c17.7 0 32-14.3 32-32V397.3c0-8.5-3.4-16.6-9.4-22.6L553.4 201.4zM568 753c0 3.8-3.4 7-7.5 7h-225c-4.1 0-7.5-3.2-7.5-7v-42c0-3.8 3.4-7 7.5-7h225c4.1 0 7.5 3.2 7.5 7v42z m0-220c0 3.8-3.4 7-7.5 7H476v84.9c0 3.9-3.1 7.1-7 7.1h-42c-3.8 0-7-3.2-7-7.1V540h-84.5c-4.1 0-7.5-3.2-7.5-7v-42c0-3.9 3.4-7 7.5-7H420v-84.9c0-3.9 3.2-7.1 7-7.1h42c3.9 0 7 3.2 7 7.1V484h84.5c4.1 0 7.5 3.1 7.5 7v42z - M599.22969 424.769286 599.22969 657.383158 424.769286 831.844585 424.769286 424.769286 192.155415 192.155415 831.844585 192.155415Z - M71.111111 1024V0h661.333333L952.888889 219.420444V1024H71.111111z m808.305778-731.420444l-220.444445-219.448889H144.583111V950.897778h734.833778V292.579556zM438.528 512h-220.444444V219.420444h220.444444V512z m-73.500444-219.420444H291.555556v146.289777h73.472v-146.289777z m0 512h73.500444v73.130666h-220.444444v-73.130666H291.555556v-146.289778H218.083556V585.102222h146.944v219.448889z m293.944888-365.710223h73.472V512H512v-73.130667h73.472v-146.289777H512V219.420444h146.972444v219.448889z m73.472 438.840889H512V585.130667h220.444444v292.579555z m-73.472-219.420444h-73.500444v146.289778h73.500444v-146.289778z - - M1024 1024H0V0h1024v1024z m-64-64V320H320V256h640V64H64v896h192V64h64v896z - M81.92 81.92v860.16h860.16V81.92H81.92z m802.304 57.856V322.56H139.776V139.776h744.448z m-744.448 240.64H322.56v503.808H139.776V380.416z m240.128 503.808V380.416h504.32v503.808H379.904z - - M1024 896v128H0V704h128v192h768V704h128v192zM576 554.688L810.688 320 896 405.312l-384 384-384-384L213.312 320 448 554.688V0h128v554.688z - M432 0h160c26.6 0 48 21.4 48 48v336h175.4c35.6 0 53.4 43 28.2 68.2L539.4 756.6c-15 15-39.6 15-54.6 0L180.2 452.2c-25.2-25.2-7.4-68.2 28.2-68.2H384V48c0-26.6 21.4-48 48-48z m592 752v224c0 26.6-21.4 48-48 48H48c-26.6 0-48-21.4-48-48V752c0-26.6 21.4-48 48-48h293.4l98 98c40.2 40.2 105 40.2 145.2 0l98-98H976c26.6 0 48 21.4 48 48z m-248 176c0-22-18-40-40-40s-40 18-40 40 18 40 40 40 40-18 40-40z m128 0c0-22-18-40-40-40s-40 18-40 40 18 40 40 40 40-18 40-40z - M592 768h-160c-26.6 0-48-21.4-48-48V384h-175.4c-35.6 0-53.4-43-28.2-68.2L484.6 11.4c15-15 39.6-15 54.6 0l304.4 304.4c25.2 25.2 7.4 68.2-28.2 68.2H640v336c0 26.6-21.4 48-48 48z m432-16v224c0 26.6-21.4 48-48 48H48c-26.6 0-48-21.4-48-48V752c0-26.6 21.4-48 48-48h272v16c0 61.8 50.2 112 112 112h160c61.8 0 112-50.2 112-112v-16h272c26.6 0 48 21.4 48 48z m-248 176c0-22-18-40-40-40s-40 18-40 40 18 40 40 40 40-18 40-40z m128 0c0-22-18-40-40-40s-40 18-40 40 18 40 40 40 40-18 40-40z - M961.3 319.6L512 577.3 62.7 319.6 512 62l449.3 257.6zM512 628.4L185.4 441.6 62.7 512 512 769.6 961.3 512l-122.7-70.4L512 628.4zM512 820.8L185.4 634 62.7 704.3 512 962l449.3-257.7L838.6 634 512 820.8z - M295.328 472l143.184 276.032S671.184 186.992 1038.096 0c-8.944 133.568-44.8 249.328 17.904 391.792C894.912 427.408 563.792 828.112 456.4 1024 304.272 837.008 125.296 694.544 0 650.016z - M89.6 806.4h844.8V217.6H89.6v588.8zM0 128h1024v768H0V128z m242.816 577.536L192 654.72l154.304-154.368L192 346.048l50.816-50.816L448 500.352 242.816 705.536z m584.32 13.248H512V640h315.072v78.72z - M508.928 556.125091l92.904727 148.759273h124.462546l-79.639273-79.173819 49.245091-49.524363 164.584727 163.700363-164.631273 163.002182-49.152-49.617454 79.36-78.568728h-162.955636l-95.650909-153.227636 41.472-65.349818z m186.973091-394.705455l164.584727 163.700364-164.631273 163.002182-49.152-49.617455L726.109091 359.936H529.687273l-135.540364 223.976727H139.636364v-69.818182h215.133091l135.586909-223.976727h235.938909l-79.639273-79.173818 49.245091-49.524364z - - M795.968 471.04A291.584 291.584 0 0 0 512 256a293.376 293.376 0 0 0-283.968 215.04H0v144h228.032A292.864 292.864 0 0 0 512 832a291.136 291.136 0 0 0 283.968-216.96H1024V471.04h-228.032M512 688A145.856 145.856 0 0 1 366.016 544 144.576 144.576 0 0 1 512 400c80 0 145.984 63.104 145.984 144A145.856 145.856 0 0 1 512 688 - M0 586.459429l403.968 118.784 497.517714-409.892572-385.536 441.490286-1.609143 250.587428 154.916572-204.580571 278.601143 83.456L1170.285714 36.571429z - M24.356571 512A488.155429 488.155429 0 0 1 512 24.356571 488.155429 488.155429 0 0 1 999.643429 512 488.155429 488.155429 0 0 1 512 999.643429 488.155429 488.155429 0 0 1 24.356571 512z m446.976-325.046857v326.656L242.614857 619.227429l51.126857 110.665142 299.52-138.24V186.953143H471.332571z - M714.624 253.648h-404.8l-57.808 57.328h520.48z m-491.568 85.984v200.624h578.336V339.632z m404.8 143.296h-28.88v-28.64H425.472v28.64h-28.912v-57.312h231.328v57.312z m-404.8 295.12h578.336V559.36H223.056z m173.504-132.704h231.328v57.328h-28.912v-28.656H425.472v28.656h-28.912v-57.328z - M868.736 144.96a144.64 144.64 0 1 0-289.408 0c0 56.064 32.64 107.008 83.456 130.624-4.928 95.552-76.608 128-201.088 174.592-52.48 19.712-110.336 41.6-159.744 74.432V276.16A144.448 144.448 0 0 0 241.664 0.192a144.64 144.64 0 0 0-144.64 144.768c0 58.24 34.688 108.288 84.352 131.2v461.184a144.32 144.32 0 0 0-84.416 131.2 144.704 144.704 0 1 0 289.472 0 144.32 144.32 0 0 0-83.52-130.688c4.992-95.488 76.672-127.936 201.152-174.592 122.368-45.952 273.792-103.168 279.744-286.784a144.64 144.64 0 0 0 84.928-131.52zM241.664 61.44a83.456 83.456 0 1 1 0 166.912 83.456 83.456 0 0 1 0-166.912z m0 890.56a83.52 83.52 0 1 1 0-167.04 83.52 83.52 0 0 1 0 167.04zM724.032 228.416a83.52 83.52 0 1 1 0-167.04 83.52 83.52 0 0 1 0 167.04z - M896 128h-64V64c0-35.2-28.8-64-64-64s-64 28.8-64 64v64h-64c-35.2 0-64 28.8-64 64s28.8 64 64 64h64v64c0 35.2 28.8 64 64 64s64-28.8 64-64V256h64c35.2 0 64-28.8 64-64s-28.8-64-64-64z m-203.52 307.2C672.64 480.64 628.48 512 576 512H448c-46.72 0-90.24 12.8-128 35.2V372.48C394.24 345.6 448 275.2 448 192c0-106.24-85.76-192-192-192S64 85.76 64 192c0 83.2 53.76 153.6 128 180.48v279.68c-74.24 25.6-128 96.64-128 179.84 0 106.24 85.76 192 192 192s192-85.76 192-192c0-66.56-33.92-124.8-84.48-159.36 22.4-19.84 51.84-32.64 84.48-32.64h128c121.6 0 223.36-85.12 248.96-199.04-18.56 4.48-37.12 7.04-56.96 7.04-26.24 0-51.2-5.12-75.52-12.8zM256 128c35.2 0 64 28.8 64 64s-28.8 64-64 64-64-28.8-64-64 28.8-64 64-64z m0 768c-35.2 0-64-28.8-64-64s28.8-64 64-64 64 28.8 64 64-28.8 64-64 64z - M901.802667 479.232v-1.024c0-133.461333-111.616-241.664-249.514667-241.664-105.813333 0-195.925333 63.829333-232.448 153.941333-27.989333-20.138667-62.464-32.426667-100.010667-32.426666-75.776 0-139.605333 49.152-159.744 116.053333-51.882667 36.522667-86.016 96.938667-86.016 165.205333 0 111.616 90.453333 201.728 201.728 201.728h503.466667c111.616 0 201.728-90.453333 201.728-201.728 0-65.194667-31.061333-123.221333-79.189333-160.085333z - M363.789474 512h67.368421v107.789474h107.789473v67.368421h-107.789473v107.789473h-67.368421v-107.789473h-107.789474v-67.368421h107.789474v-107.789474z m297.539368-64A106.671158 106.671158 0 0 1 768 554.671158C768 613.578105 719.548632 660.210526 660.210526 660.210526h-107.789473v-53.894737h-107.789474v-107.789473h-94.31579v107.789473h-94.315789c4.311579-21.194105 22.231579-46.807579 43.560421-50.755368l-0.889263-11.560421a74.671158 74.671158 0 0 1 71.248842-74.590316 128.053895 128.053895 0 0 1 238.605474-7.437473 106.172632 106.172632 0 0 1 52.816842-13.972211z - M177.311335 156.116617c-22.478967 4.729721-32.774451 17.336854-36.251645 36.893258-10.080589 56.697303-33.399691 257.604032-13.234419 277.769304l445.342858 445.341834c23.177885 23.177885 60.757782 23.178909 83.935668 0l246.019183-246.019183c23.177885-23.177885 23.177885-60.757782 0-83.935668l-445.341834-445.341834C437.419398 120.463606 231.004211 144.82034 177.311335 156.116617zM331.22375 344.221786c-26.195615 26.195615-68.667939 26.195615-94.863555 0-26.195615-26.195615-26.195615-68.666916 0-94.863555s68.667939-26.195615 94.862531 0C357.418342 275.55487 357.419366 318.02617 331.22375 344.221786z - M682.666667 536.576h-143.701334v-142.336h-142.336V283.306667H238.933333a44.032 44.032 0 0 0-40.96 40.96v170.666666a55.978667 55.978667 0 0 0 14.336 34.133334l320.512 320.512a40.96 40.96 0 0 0 57.685334 0l173.738666-173.738667a40.96 40.96 0 0 0 0-57.685333z m-341.333334-108.544a40.96 40.96 0 1 1 0-57.685333 40.96 40.96 0 0 1 0 57.685333zM649.216 284.330667V141.994667h-68.608v142.336h-142.336v68.266666h142.336v142.336h68.608v-142.336h142.336v-68.266666h-142.336z - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/Border.xaml b/SourceGit/Resources/Styles/Border.xaml deleted file mode 100644 index a6f9bcd5..00000000 --- a/SourceGit/Resources/Styles/Border.xaml +++ /dev/null @@ -1,12 +0,0 @@ - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/Button.xaml b/SourceGit/Resources/Styles/Button.xaml deleted file mode 100644 index 49c6169b..00000000 --- a/SourceGit/Resources/Styles/Button.xaml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/CheckBox.xaml b/SourceGit/Resources/Styles/CheckBox.xaml deleted file mode 100644 index b271d993..00000000 --- a/SourceGit/Resources/Styles/CheckBox.xaml +++ /dev/null @@ -1,38 +0,0 @@ - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/ComboBox.xaml b/SourceGit/Resources/Styles/ComboBox.xaml deleted file mode 100644 index 5fd3e228..00000000 --- a/SourceGit/Resources/Styles/ComboBox.xaml +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/ContextMenu.xaml b/SourceGit/Resources/Styles/ContextMenu.xaml deleted file mode 100644 index c82d6fee..00000000 --- a/SourceGit/Resources/Styles/ContextMenu.xaml +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/DataGrid.xaml b/SourceGit/Resources/Styles/DataGrid.xaml deleted file mode 100644 index 88bc5450..00000000 --- a/SourceGit/Resources/Styles/DataGrid.xaml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/HyperLink.xaml b/SourceGit/Resources/Styles/HyperLink.xaml deleted file mode 100644 index db1e0330..00000000 --- a/SourceGit/Resources/Styles/HyperLink.xaml +++ /dev/null @@ -1,11 +0,0 @@ - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/Label.xaml b/SourceGit/Resources/Styles/Label.xaml deleted file mode 100644 index 7fecb8de..00000000 --- a/SourceGit/Resources/Styles/Label.xaml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/ListView.xaml b/SourceGit/Resources/Styles/ListView.xaml deleted file mode 100644 index 72ab8ba5..00000000 --- a/SourceGit/Resources/Styles/ListView.xaml +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/Path.xaml b/SourceGit/Resources/Styles/Path.xaml deleted file mode 100644 index d3f643e6..00000000 --- a/SourceGit/Resources/Styles/Path.xaml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/RadioButton.xaml b/SourceGit/Resources/Styles/RadioButton.xaml deleted file mode 100644 index d7b65747..00000000 --- a/SourceGit/Resources/Styles/RadioButton.xaml +++ /dev/null @@ -1,52 +0,0 @@ - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/ScrollBar.xaml b/SourceGit/Resources/Styles/ScrollBar.xaml deleted file mode 100644 index 25c06ecf..00000000 --- a/SourceGit/Resources/Styles/ScrollBar.xaml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/ScrollViewer.xaml b/SourceGit/Resources/Styles/ScrollViewer.xaml deleted file mode 100644 index 971545e7..00000000 --- a/SourceGit/Resources/Styles/ScrollViewer.xaml +++ /dev/null @@ -1,54 +0,0 @@ - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/TabControl.xaml b/SourceGit/Resources/Styles/TabControl.xaml deleted file mode 100644 index 1a449039..00000000 --- a/SourceGit/Resources/Styles/TabControl.xaml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/TextBox.xaml b/SourceGit/Resources/Styles/TextBox.xaml deleted file mode 100644 index 48a96a72..00000000 --- a/SourceGit/Resources/Styles/TextBox.xaml +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/ToggleButton.xaml b/SourceGit/Resources/Styles/ToggleButton.xaml deleted file mode 100644 index b12157d6..00000000 --- a/SourceGit/Resources/Styles/ToggleButton.xaml +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/Tooltip.xaml b/SourceGit/Resources/Styles/Tooltip.xaml deleted file mode 100644 index 0e7f9d3e..00000000 --- a/SourceGit/Resources/Styles/Tooltip.xaml +++ /dev/null @@ -1,21 +0,0 @@ - - - \ No newline at end of file diff --git a/SourceGit/Resources/Styles/TreeView.xaml b/SourceGit/Resources/Styles/TreeView.xaml deleted file mode 100644 index f8b9b802..00000000 --- a/SourceGit/Resources/Styles/TreeView.xaml +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Themes/Dark.xaml b/SourceGit/Resources/Themes/Dark.xaml deleted file mode 100644 index 2f8cac59..00000000 --- a/SourceGit/Resources/Themes/Dark.xaml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SourceGit/Resources/Themes/Light.xaml b/SourceGit/Resources/Themes/Light.xaml deleted file mode 100644 index 2d936bb2..00000000 --- a/SourceGit/Resources/Themes/Light.xaml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SourceGit/SourceGit.csproj b/SourceGit/SourceGit.csproj deleted file mode 100644 index 896fbe8e..00000000 --- a/SourceGit/SourceGit.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - net46 - WinExe - true - true - App.ico - sourcegit - OpenSource GIT client for Windows - Copyright © sourcegit 2020. All rights reserved. - App.manifest - 1.5 - MIT - - - AnyCPU - true - - - - - \ No newline at end of file diff --git a/SourceGit/UI/About.xaml b/SourceGit/UI/About.xaml deleted file mode 100644 index 823d9b13..00000000 --- a/SourceGit/UI/About.xaml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SourceGit/UI/About.xaml.cs b/SourceGit/UI/About.xaml.cs deleted file mode 100644 index 4e3a0f90..00000000 --- a/SourceGit/UI/About.xaml.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Diagnostics; -using System.Reflection; -using System.Windows; -using System.Windows.Navigation; - -namespace SourceGit.UI { - - /// - /// About dialog - /// - public partial class About : Window { - - /// - /// Current app version - /// - public string Version { - get { - return "VERSION : " + FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion; - } - } - - /// - /// Constructor - /// - public About() { - InitializeComponent(); - } - - /// - /// Open source code link - /// - /// - /// - private void OpenSource(object sender, RequestNavigateEventArgs e) { - Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)); - e.Handled = true; - } - - /// - /// Close this dialog - /// - private void Quit(object sender, RoutedEventArgs e) { - Close(); - } - } -} diff --git a/SourceGit/UI/AddSubmodule.xaml b/SourceGit/UI/AddSubmodule.xaml deleted file mode 100644 index a6bd9a53..00000000 --- a/SourceGit/UI/AddSubmodule.xaml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/SourceGit/UI/Blame.xaml.cs b/SourceGit/UI/Blame.xaml.cs deleted file mode 100644 index abf44055..00000000 --- a/SourceGit/UI/Blame.xaml.cs +++ /dev/null @@ -1,240 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Animation; - -namespace SourceGit.UI { - - /// - /// Viewer to show git-blame - /// - public partial class Blame : Window { - - /// - /// Background color for blocks. - /// - public static Brush[] BG = new Brush[] { - Brushes.Transparent, - new SolidColorBrush(Color.FromArgb(128, 0, 0, 0)) - }; - - /// - /// Constructor - /// - /// - /// - /// - public Blame(Git.Repository repo, string file, string revision) { - InitializeComponent(); - - double minWidth = content.ActualWidth; - - // Move to center. - var parent = App.Current.MainWindow; - Left = parent.Left + (parent.Width - Width) * 0.5; - Top = parent.Top + (parent.Height - Height) * 0.5; - - // Show loading. - DoubleAnimation anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1)); - anim.RepeatBehavior = RepeatBehavior.Forever; - loading.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, anim); - loading.Visibility = Visibility.Visible; - - // Layout content - blameFile.Content = $"{file}@{revision.Substring(0, 8)}"; - Task.Run(() => { - var blame = repo.BlameFile(file, revision); - - Dispatcher.Invoke(() => { - content.Document.Blocks.Clear(); - - if (blame.IsBinary) { - lineNumber.Text = "0"; - - Paragraph p = new Paragraph(new Run("BINARY FILE BLAME NOT SUPPORTED!!!")); - p.Margin = new Thickness(0); - p.Padding = new Thickness(0); - p.LineHeight = 1; - p.Background = Brushes.Transparent; - p.Foreground = FindResource("Brush.FG") as SolidColorBrush; - p.FontStyle = FontStyles.Normal; - - content.Document.Blocks.Add(p); - } else { - List numbers = new List(); - for (int i = 0; i < blame.LineCount; i++) numbers.Add(i.ToString()); - lineNumber.Text = string.Join("\n", numbers); - numbers.Clear(); - - for (int i = 0; i < blame.Blocks.Count; i++) { - var frag = blame.Blocks[i]; - var idx = i; - - Paragraph p = new Paragraph(new Run(frag.Content)); - p.DataContext = frag; - p.Margin = new Thickness(0); - p.Padding = new Thickness(0); - p.LineHeight = 1; - p.Background = BG[i % 2]; - p.Foreground = FindResource("Brush.FG") as SolidColorBrush; - p.FontStyle = FontStyles.Normal; - p.ContextMenuOpening += (sender, ev) => { - if (!content.Selection.IsEmpty) return; - - Hyperlink link = new Hyperlink(new Run(frag.CommitSHA)); - link.ToolTip = "CLICK TO GO"; - link.Click += (o, e) => { - repo.OnNavigateCommit?.Invoke(frag.CommitSHA); - e.Handled = true; - }; - - foreach (var block in content.Document.Blocks) { - var paragraph = block as Paragraph; - if ((paragraph.DataContext as Git.Blame.Block).CommitSHA == frag.CommitSHA) { - paragraph.Background = Brushes.Green; - } else { - paragraph.Background = BG[i % 2]; - } - } - - commitID.Content = link; - authorName.Content = frag.Author; - authorTime.Content = frag.Time; - popup.IsOpen = true; - ev.Handled = true; - }; - - var formatter = new FormattedText( - frag.Content, - CultureInfo.CurrentUICulture, - FlowDirection.LeftToRight, - new Typeface(content.FontFamily, p.FontStyle, p.FontWeight, p.FontStretch), - content.FontSize, - Brushes.Black, - new NumberSubstitution(), - TextFormattingMode.Ideal); - if (minWidth < formatter.Width) { - content.Document.PageWidth = formatter.Width + 16; - minWidth = formatter.Width; - } - - content.Document.Blocks.Add(p); - } - } - - // Hide loading. - loading.RenderTransform.BeginAnimation(RotateTransform.AngleProperty, null); - loading.Visibility = Visibility.Collapsed; - }); - }); - } - - /// - /// Click logo - /// - /// - /// - private void LogoMouseButtonDown(object sender, MouseButtonEventArgs e) { - var element = e.OriginalSource as FrameworkElement; - if (element == null) return; - - var pos = PointToScreen(new Point(0, 33)); - SystemCommands.ShowSystemMenu(this, pos); - } - - /// - /// Minimize - /// - private void Minimize(object sender, RoutedEventArgs e) { - SystemCommands.MinimizeWindow(this); - } - - /// - /// Maximize/Restore - /// - private void MaximizeOrRestore(object sender, RoutedEventArgs e) { - if (WindowState == WindowState.Normal) { - SystemCommands.MaximizeWindow(this); - } else { - SystemCommands.RestoreWindow(this); - } - } - - /// - /// Quit - /// - private void Quit(object sender, RoutedEventArgs e) { - Close(); - } - - /// - /// Sync scroll - /// - /// - /// - private void SyncScrollChanged(object sender, ScrollChangedEventArgs e) { - if (e.VerticalChange != 0) { - var margin = new Thickness(4, -e.VerticalOffset, 4, 0); - lineNumber.Margin = margin; - } - } - - /// - /// Mouse wheel - /// - /// - /// - private void MouseWheelOnContent(object sender, MouseWheelEventArgs e) { - if (e.Delta > 0) { - content.LineUp(); - } else { - content.LineDown(); - } - - e.Handled = true; - } - - /// - /// Content size changed. - /// - /// - /// - private void ContentSizeChanged(object sender, SizeChangedEventArgs e) { - if (content.Document.PageWidth < content.ActualWidth) { - content.Document.PageWidth = content.ActualWidth; - } - } - - /// - /// Auto scroll when selection changed. - /// - /// - /// - private void ContentSelectionChanged(object sender, RoutedEventArgs e) { - var doc = sender as RichTextBox; - if (doc == null || doc.IsFocused == false) return; - - if (Mouse.LeftButton == MouseButtonState.Pressed && !doc.Selection.IsEmpty) { - var p = Mouse.GetPosition(doc); - - if (p.X <= 8) { - doc.LineLeft(); - } else if (p.X >= doc.ActualWidth - 8) { - doc.LineRight(); - } - - if (p.Y <= 8) { - doc.LineUp(); - } else if (p.Y >= doc.ActualHeight - 8) { - doc.LineDown(); - } - } - } - } -} diff --git a/SourceGit/UI/CherryPick.xaml b/SourceGit/UI/CherryPick.xaml deleted file mode 100644 index c22fd9b7..00000000 --- a/SourceGit/UI/CherryPick.xaml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - -