Class: GitReclone

Inherits:
Object
  • Object
show all
Defined in:
lib/git-reclone-version.rb,
lib/git-reclone.rb

Overview

universal version tracking

Constant Summary collapse

HELP =
"\#{\"git reclone\".red}: a git repo restoring tool\n  \nreplaces your local copy with a fresh clone from the remote.\nclones to a temp directory first, so your local copy is safe if the clone fails.\nto restore from a particular remote repository, specify the host:\n\n    git reclone github    # reclone using github\n    git reclone bitbucket # reclone using bitbucket\n    git reclone gitea     # reclone using gitea\n    git reclone gogs      # reclone using gogs\n"
VERSION =
"1.0.0"

Instance Method Summary collapse

Constructor Details

#initialize(test = false) ⇒ GitReclone

Returns a new instance of GitReclone.



14
15
16
17
18
# File 'lib/git-reclone.rb', line 14

def initialize(test = false)
  @pdelay = 0.01 # constant for arrow speed
  @testing = test
  @verify = !test
end

Instance Method Details

#current_branchObject



56
57
58
# File 'lib/git-reclone.rb', line 56

def current_branch
  `git rev-parse --abbrev-ref HEAD`.chomp
end

#fire(args = []) ⇒ Object



20
21
22
23
24
25
# File 'lib/git-reclone.rb', line 20

def fire(args = [])
  opts = args.select { |a| a[0] == "-" }
  opts.each { |o| parse_opt o }
  exit 0 if @testing || opts.first
  parse_arg((args - opts).first)
end

#git_rootObject



52
53
54
# File 'lib/git-reclone.rb', line 52

def git_root
  `git rev-parse --show-toplevel`
end

#no_repo?Boolean

Returns:

  • (Boolean)


47
48
49
50
# File 'lib/git-reclone.rb', line 47

def no_repo?
  `git status 2>&1`.split("\n").first ==
    "fatal: Not a git repository (or any of the parent directories): .git"
end

#parse_arg(a) ⇒ Object



43
44
45
# File 'lib/git-reclone.rb', line 43

def parse_arg(a)
  a.nil? ? verify(remote) : verify(remote(a))
end

#parse_opt(o) ⇒ Object



32
33
34
35
36
37
38
39
40
41
# File 'lib/git-reclone.rb', line 32

def parse_opt(o)
  case o
  when "--force", "-f"
    @verify = false
  when "--help", "-h"
    puts GitReclone::HELP
  when "--version", "-v"
    puts GitReclone::VERSION
  end
end

#pexit(msg) ⇒ Object



27
28
29
30
# File 'lib/git-reclone.rb', line 27

def pexit(msg)
  puts msg
  exit 1
end

#reclone(remote, root, branch = nil) ⇒ Object

overwrite the local copy of the repository with the remote one



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/git-reclone.rb', line 114

def reclone(remote, root, branch = nil)
  # create a temporary directory for cloning
  temp_dir = Dir.mktmpdir("git-reclone-")

  begin
    # clone into temp directory (disable credential prompts)
    cloner = "GIT_TERMINAL_PROMPT=0 git clone \"#{remote}\" \"#{temp_dir}\" > /dev/null 2>&1"

    if system(cloner)
      # clone succeeded, now replace the local copy
      if !@testing
        tree = Dir.glob("*", File::FNM_DOTMATCH).select { |d| ![".", ".."].include? d }
        FileUtils.rmtree(tree)

        # move contents from temp to root
        Dir.glob("#{temp_dir}/*", File::FNM_DOTMATCH).each do |item|
          next if File.basename(item) == "." || File.basename(item) == ".."
          FileUtils.mv(item, root)
        end

        # restore original branch if it exists
        if branch && branch != "HEAD"
          Dir.chdir(root) do
            system("git checkout #{branch} > /dev/null 2>&1")
          end
        end
      end

      puts "Recloned successfully.".green
      "Recloned successfully.".green
    else
      # clone failed
      puts "Clone failed.".red
      nil
    end
  ensure
    # always clean up temp directory
    FileUtils.rm_rf(temp_dir) if Dir.exist?(temp_dir)
  end
end

#reclonebannerObject



64
65
66
67
68
# File 'lib/git-reclone.rb', line 64

def reclonebanner
  25.times { |x| slowp "\rpreparing| ".red << "~" * x << "#==>".red }
  25.times { |x| slowp "\rpreparing| ".red << " " * x << "~" * (25 - x) << "#==>".yellow }
  printf "\rREADY.".red << " " * 50 << "\n"
end

#remote(search = /.*/) ⇒ Object

trying to parse out which remote should be the new source



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/git-reclone.rb', line 76

def remote(search = /.*/)
  pexit "Not currently in a git repository.".yellow if no_repo?

  r = remotes.find { |gr| gr.match search }

  pexit "No remotes found in this repository.".yellow if remotes.nil?

  if r.nil?
    errmsg = "No remotes found that match #{search.to_s.red}. All remotes:\n" + remotes.join("\n")
    pexit errmsg
    errmsg
  else
    r
  end
end

#remotesObject



60
61
62
# File 'lib/git-reclone.rb', line 60

def remotes
  `git remote -v`.split("\n").map { |r| r.split[1] }.uniq
end

#slowp(x) ⇒ Object



70
71
72
73
# File 'lib/git-reclone.rb', line 70

def slowp(x)
  sleep @pdelay
  printf x
end

#verify(r) ⇒ Object

show remote to user and confirm location (unless using -f)



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/git-reclone.rb', line 93

def verify(r)
  reclonebanner
  puts "Remote source:\t".red << r
  puts "Local target:\t".red << git_root

  branch = current_branch
  puts "Current branch:\t".red << branch unless branch == "HEAD"

  if @verify
    puts "Warning: this will replace the local copy with a fresh clone from the remote.".yellow
    printf "Continue recloning local repo? [yN] ".yellow
    unless $stdin.gets.chomp.downcase[0] == "y"
      puts "Reclone aborted.".green
      return
    end
  end

  reclone remote, git_root.chomp, branch unless @testing
end